黑马程序员_【总结】_ 高新_6_代理类(高新end)

代理类


---------- android培训 java培训 、期待与您交流! ---------- 

---------------------------------------------------------------------------------------------------------------------------------------------
代理类
1、通俗来说,代理就是要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,
例如:异常处理、日志、计算方法的运行时间、事务管理、等等。
2、动态代理:在程序运行时,运用反射机制动态创建而成。
3、在实际开发中,采用工厂设计模式和配置文件的方法进行对管理,则不需要修改客户端,
在配置文件可自由切换目标类、功能类。
4、JAV生成动态类,必须实现一个或者多个接口,且只能用做具体有相同接口的目标类的代理
5、生成的代理类只有一个有参构造方法,并且参数类型是:InvocationHandler对象
6、调用打印方法 toString() ,返回的是 null ,调用这个对象的方法,只能调用无返回值类型的方法 
7、动态代理类接收目标,和系统功能对象,即可完成代理功能,只需要构建系统功能即可
--------------------------------------------------
JAVA的动态代理 
代理模式 
代理模式是常用的java设计模式,
1、他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、
把消息转发给委托类,以及事后处理消息等。
2、代理类与委托类之间通常会存在关联关系,
一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是
通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。 
静态代理、动态代理。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,
代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。


通俗来说,代理就是要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能
例如:异常处理、日志、计算方法的运行时间、事务管理、等等。

代理类要调用目标类的功能。

目标类  doSomeThing(){
业务功能代码
            }
代理类  doSomeThing(){
          //前置系统功能代码
           目标对象.doSomeThing()
          //后置系统功能代码   
         }
在实际开发中,采用工厂设计模式和配置文件的方法进行对管理,则不需要修改客户端,
在配置文件可自由切换目标类、功能类。
面向方面的编程     Aspect Oriented Program  >>   AOP
交叉业务:
系统存在交叉业务,一个交叉业务就是切入系统中的一个方面,不如,安全、事物、
日志等功能,要贯穿到多个模块中


AOP 目标就是要使交叉业务模块化
1、JAV生成动态类,必须实现一个或者多个接口,且只能用做具体有相同接口的目标类的代理
2、CGLIB 库可以动态生成一个类的子类,子类可以用作该类的代理类
  CGLIB可以为一个没有实现接口的类生成代理类
3、代理类出了调用目标相同的方法和放回目标返回的结果外
还可以在下面4个位置加系统功能代码:
1、调用目标方法前
2、调用目标方法后
3、调用目标方法前后
4、在处理目标方法异常的 catch 中


生成的代理类只有一个有参构造方法
并且参数类型是:InvocationHandler

Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader,Collection.class);
   参数一:类加载器                   参数二:接口字节码对象
/【A】代理类>>动态代理类
public class ProxyTest {
	public static void main(String[] args) throws Exception {
		//一、获取      Proxy 的代理类 并传递 类加载器,和接口
		Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		System.out.println(clazz.getName());
		
		System.out.println("--------Proxy constructor 1 ------------ ");
		//二、
		//2、获取该代理类的 构造方法
		Constructor[] constructors = clazz.getConstructors();
		
		for(Constructor constructor : constructors){
			//2-1 name  接收构造方法名
			String name = constructor.getName();
			//2-2 把该 构造函数方法名传递给 StringBuilder
			StringBuilder sb = new StringBuilder(name);
			//2-3 在 name 后追加 (      即name(
			sb.append('(');
			//2-5  获取 参数列表数组
			Class [] clazzParams = constructor.getParameterTypes();
			// 2-6 循环取出
			for(Class clazzParam : clazzParams){
				// 2-7  添加每个一个 参数名 并添加  分号
				sb.append(clazzParam.getName()).append(',');
			}
			//2-8 去掉最后一个 分号    添加条件判断参数列表是否为空或者是1个参数
			if(clazzParams != null && clazzParams.length!= 0 )
				// 2-9 删除 参数后面的 分号
				sb.deleteCharAt(sb.length()-1);
			//2-4追加         即可    name(参数)
			sb.append(')');
			//2-10 打印
			System.out.println(sb.toString());
		}
		
		// 三  方法 同上。
		System.out.println("--------Proxy Method 2 ------------");
		
		Method[] methods = clazz.getMethods();
		
		for(Method method : methods){
			String name = method.getName();
			StringBuilder sb = new StringBuilder(name);
			sb.append('(');
			// 获取参数列表
			Class [] clazzParams = method.getParameterTypes();
			for(Class clazzParam : clazzParams){
				//追加 每一个参数
				sb.append(clazzParam.getName()).append(',');
			}
			//无参,或者只有1个参数,则删除 最后一位的   ‘,’符号
			if(clazzParams != null && clazzParams.length!= 0 )
				sb.deleteCharAt(sb.length()-1);
			sb.append(')');
			
			System.out.println(sb.toString());
		}
		
		//三、
		System.out.println("----------------[begin creat instance Proxy 3]------------------");
		//		获得一个动态代理类,用这个动态类 创建实例对象
		//		注意:动态类 构造方法要接收一个  InvocationHandler  对象,    (因为尅心汉得内儿
		//			调用前要准备好一个自定义 InvocationHandler 对象
		//		调用打印方法 toString() ,返回的是 null 调用这个对象的方法,只能调用无返回值类型的方法 
				
				//3-1 获取 InvocationHandler   的构造函数
				Constructor constructor = clazz.getConstructor(InvocationHandler.class);
				//3-2 创建一个方法,实现  InvocationHandler  并对其重构一个方法
				class MyInvocationHandler1 implements InvocationHandler{
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						return null;
					}		
				}
				//3-3 Collection 接收 这个构造函数对象.newInstance
				Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());//传递 自定义InvocationHandler 对象
				//3-4 打印结果为null  
				System.out.println(proxy1+"\n该null   说明是有InvocationHandler对象的,只不过 toString 方法返回的值为null");
				//注意,如果是对象为null,会报空指针异常,   也就说说,对象有,toString方法返回的是null
				
				//3-5 调用无返回值方法void   
				proxy1.clear();
				//3-6有返回值方法 
				//System.out.println(proxy1.size());
				//发现,无返回值的方法可以调用, 有返回值的方法会报错!!!
				
			//四
				System.out.println("----------------[4、代码优化]------------------");
				//【】3-2 到3-3  可以用匿名内部类来完成 
				
				Collection proxy2 = (Collection) constructor.newInstance(new InvocationHandler(){
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						return null;
					}
				});
			//五
				System.out.println("----------------[5、代码优化]------------------\n>>无目标的  动态代理类");
				//【】超级进化版本  Proxy 提供的一个静态方法 【创建某一接口代理类 最简单方法:】
				//Foo f = (Foo)Proxy.newProxyInstance(loader, interfaces, h)  
				//三个参数:类加载器、接口数组、InvocationHandler 内部类
				Collection proxy3 = (Collection)Proxy.newProxyInstance(
						Collection.class.getClassLoader(),
						new Class[]{Collection.class},
						new InvocationHandler(){
							public Object invoke(Object proxy, Method method, Object[] args)
									throws Throwable {
								return null;
							}
						}
						);
				// 无目标的  动态代理类
			//六	  为代理类指定目标  并添加系统功能
				System.out.println("----------------[6]------------------\n>>有目标 有系统功能的  动态代理类");
				//指定目标
				Collection proxy4 = (Collection)Proxy.newProxyInstance(
						Collection.class.getClassLoader(),
						new Class[]{Collection.class},
						new InvocationHandler(){
							//ArrayList target = new ArrayList(); //【b】成员变量size()方法打印 2
							public Object invoke(Object proxy, Method method, Object[] args)
									throws Throwable {
								//指点目标
								ArrayList target = new ArrayList(); // 【a】局部变量size()方法打印 0
								long beginTime = System.currentTimeMillis();//开始计时
								Thread.sleep(1000);//因为太快,所以设定一个延迟,方便看效果。
								Object retVal = method.invoke(target, args);
								long ennTime = System.currentTimeMillis();//结束
								//获取使用的方法名                         //计算时间
								System.out.println(method.getName()+"  running time of :  " +(ennTime-beginTime));
								return retVal;
							}
						}
						);
				proxy4.add("Test1");
				proxy4.add("Test2");
				//System.out.println(proxy4);
				System.out.println("size:   "+proxy4.size());
				
	//			为什么 size() 是0 呢,因为ArrayList target = new ArrayList() 匿名内部类的局部变量
	//			方法每调用一次,都会new一个目标,所以是0
	//			应该把ArrayList target = new ArrayList() 放到方法之上,成为一个成员变量
				
				
				System.out.println("----------------[Proxy  7]------------------");
				//七【】终极进化版本  >>  定义: getProxy( 【目标】传递,【系统功能】对象	);
				//
				//7-2、把目标抽调出来  内部类想要调用外部类成员变量必须在成员变量前加final
				 final ArrayList target = new ArrayList();
				 //2、把功能代码封装为一个 方法。MyAdvice();
				 //3  传递目标,功能对象  给    getProxy   
				 //达到快捷功能 复用性极好 ,只需要给不同的目标,和构建不同的功能代码即可
				Collection proxy5 = (Collection)getProxy(target,new MyAdvice());
				
				proxy5.add("Test3");
				proxy5.add("Test4");
				System.out.println("getProxy>>size:  "+proxy5.size());
	}
	//通过传递目标和结束 系统功能对象 使该方法成为固定代理类。
	//使用的时候只需要写MyAdvice ,配置文件配置目标  即可 。
	//【七: 7-1 获取动态代理  参数有2个:目标、系统功能对象】
	public static Object getProxy(final Object target,final Advice advice) {
		Object proxy =Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),
				new InvocationHandler(){
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						advice.beforeMethod(method);
						Thread.sleep(1000);
						Object retVla = method.invoke(target, args);
						advice.afterMethod(method);
						return retVla;
					}
				});
		return proxy;
	}
}//【A】end



// 【C】实现自定义接口 Advice  并实现两个方法  --   系统功能--
public class MyAdvice implements Advice {
	long beginTime ;
	//之后  这里 功能代码为 记录结束时间,计算 所用时间
	public void afterMethod(Method method) {
		System.out.println("\tover");
		long endTime = System.currentTimeMillis();
		System.out.println("\t"+method.getName()+" times :"+(endTime - beginTime));
	}
	//开始前    这里功能代码为 记时
	public void beforeMethod(Method method) {
		System.out.println("start");
		beginTime = System.currentTimeMillis();
	}
}
//【B】定义接口  Advice  
public interface Advice {
	//开始前
	void beforeMethod(Method method);
	//之后
	void afterMethod(Method method);
}
根据上述
新建一个小小的练习。调用上面定义好的   代理类
public static void prosyTest(){
		//手写该代码。
		System.out.println("----------------[动态代理类练习]------------------");
		HashSet target = new HashSet();
		Collection proxy = (Collection)ProxyTest.getProxy(target,new MyAdvice());
						//ProxyTest.getProxy 调用的是ProxyTest自定义的的代理类
		proxy.add("dd");
		proxy.add("mm");
		proxy.add("ff");
		proxy.add("ff222");
		proxy.add("ff");
		System.out.println(	"集合个数 :"+proxy.size());
		//运行观察, add的时候5次,实际只有4个元素,因为 hashMap 元素不可重复
	}
【案例:】
【采用工厂设计模式和配置文件的方法进行对管理,则不需要修改客户端,
在配置文件可自由切换目标类、功能类。】

  AOP
  合计一共6步,1-4 为当前步骤, 56 为先期完成

  可以先完成2,5,6  >>5,6是在下一个项目中拷贝过来的。
1、BeanFactory,beand 工厂
构造方法接收路径 进行 加载文件
getBean 方法
1、拿到类名--2、拿到该类的Class对象--3、创建一个bean 接收该Class对象实例对象--
4、对bean进行 instanceof 判断 是否属于 ProxyFactoryBean 类型--5、强制转换 bean为该类型
6、通过prop 获取value 值后 通过 forName(value)创建实例对象 复制给 Advice
7、target 同上-- 8、ProxyFactoryBean方法 设定 target、advice  值得
9、这个时候调用ProxyFactoryBean 的getProxy  就取得了一个 代理类

2、ProxyFactoryBean
定义接收和设定 目标变量、方法,系统功能变量、方法
动态代理类
3、AopFramworkTest 测试类
getResourceAsStream:相对路径 ,查找具有给定名称的资源。返回 inputstream
4、配置文件
设定 原 类、Advice、target 等属性          
方便      源文件和 代理类之间自由切换
5、 Advice 接口
6、MyAdvice 实现 Advice 接口  系统功能代码都在此类中完成
方法一般为4个  
1、目标功能前 2、目标功能后 3、目标功能前后 4、目标方法异常的catch中

 【1】 bean  工厂     

仔细阅读代码。
public class BeanFactory {
	Properties prop = new Properties();
	 //1-1 构造方法接收 一个路径
	public BeanFactory(InputStream ips){
		try {
			prop.load(ips);
		} catch (IOException e) {
			// 在这里异常处理就简单化了。
			e.printStackTrace();
		}
	}
	//1-2
	public Object getBean(String name){		
		//1-2-1   拿到类名
		String className = prop.getProperty(name); 
		Object bean = null;
		try {
			//1-2-2   返回className的Class对象
			Class clazz = Class.forName(className); 
			//1-2-3      返回实例对象, 对 Java bean 来说必须有一个不带参数的构造方法
			bean = clazz.newInstance();
		} catch (Exception e) {
		}
		//1-2-4 判断是否是 ProxyFactoryBean 类型
		//1-2-5  创建代理类 在ProxyFactoryBean 内
		if(bean instanceof ProxyFactoryBean){
			//1-2-6  1  end    
			//Object proxy = ((ProxyFactoryBean)bean).getProxy();//创建代理,并返回代理
			//2-3-1  强制转换 bean 对象为 ProxyFactoryBean 类型
			ProxyFactoryBean proxyFactorybean = (ProxyFactoryBean)bean;
			Object proxy =null;			
			try {
				//2-3-4 end  把 prop.getProperty(name+".advice")获取到的 系统功能配置
				//文件类名  实例化的对象强制转换为 Advice 并赋值给advice
				Advice advice = (Advice)Class.forName(prop.getProperty(name+".advice")).newInstance();
					//把prop.getProperty(name+".target") 获取到的目标类配置文件类名 实例化后 赋值给 target
				Object target = Class.forName(prop.getProperty(name+".target")).newInstance() ;
				//2-3-2 设定 目标 target 和  系统功能 advice
				proxyFactorybean .setAdvice(advice);
				proxyFactorybean.setTarget(target);
				//2-3-3   原来的     //1-2-5  
				proxy = proxyFactorybean.getProxy();// 返回  proxyFactorybean 对象的代理类
			} //catch (ClassNotFoundException e) {
			catch (Exception e) {
				e.printStackTrace();
			}
			return proxy;
		}
		return bean;
	} 
}	

//【2】ProxyFactoryBean
public class ProxyFactoryBean {
	//2-2-1
	private Advice advice;
	private Object target;
	//2-2-2  对上述 2个变量 自动 产生 get  set 方法
	public Advice getAdvice() {
		return advice;
	}
	public void setAdvice(Advice advice) {
		this.advice = advice;
	}
	public Object getTarget() {
		return target;
	}
	public void setTarget(Object target) {
		this.target = target;
	}
	//1-2-5    6是end
	public Object getProxy() {
		// 2-1 直接把前面的 动态代理类赋值过来即可
		Object proxy = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),
				new InvocationHandler(){
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						advice.beforeMethod(method);//前
						Object retVal = method.invoke(target, args);//目标方法功能方法
						advice.afterMethod(method);//后
						return retVal;
					}
				});
		return proxy;
	}
}
【4】 测试类
getResourceAsStream:查找具有给定名称的资源。返回 inputstream
getResource:查找带有给定名称的资源路径。返回URL
public class AopFramworkTest {

	public static void main(String[] args)throws Exception 
	{
		// TODO Auto-generated method stub
		InputStream ips = AopFramworkTest.class.getResourceAsStream("config.properties");
		Object bean = new BeanFactory(ips).getBean("xxx");//传递ips后,就获取 叫'xxx'的值
		System.out.println(bean.getClass().getName());//查看 这个bean是 代理,还是目标。
		//打印结果:java.util.ArryaList   是为目标
		//        :$Proxy0               是为代理类
		// 采用工厂设计模式 + 配置文件的方法进行对管理,不需要修改客户端,
		// 只需要   在配置文   件  就 可【自由切换】>>目标类、功能类。
	}

}
【3】 配置文件
 创建的个文件 config.properties 配置如下信息

 
#xxx=java.util.ArryaList     源代码
xxx=com.itheima.day6.AopFramork.ProxyFactoryBean   特殊代理类
xxx.advice=com.itheima.day6.MyAdvice      advice系统功能代码
xxx.target=java.util.ArryaList          目标


----
打印结果:java.util.ArryaList   是为目标
        :$Proxy0               是为代理类
这个案例,采用了工厂设计模式 + 配置文件的方法进行对管理,不需要修改客户端,
只需要   在配置文   件  就 可【自由切换】>>目标类、功能类























---------------------------------------------------------------------------------------------------------------------------------------------
---------- android培训、 java培训 、期待与您交流!----------


----------------------------------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值