什么代理?
一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
优点
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
AOP
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理的工作原理:
1)Client(客户端)调用代理,代理的构造方法接受一个InvocationHandler,client调用代理的各个方法,代理的各个方法请求转发给刚才通过构造方法传入的handler对象,又把各请求分发给目标的相应的方法。就是将handler封装起来,其中this引用了当前的放(发来什么请求就接受哪个方法)。
猜想分析动态生成的类的内部代码:
1、动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
2、构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:
分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?
为何动态类的实例对象的getClass()方法返回了正确结果,而没调用invoke方法:
因为代理类从Object上继承了许多方法,其中只对三个方法(hashCode、equals和toString)进行开发,委托给handler去自行处理,对于它身上其他方法不会交给代理类去实现,所以对于getClass()方法,还是由Object本身实现的。即proxy3.getClass(),该是什么结果还是什么结果,并不会交给invoke方法处理。
例子
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class ProxyTest { public static void main(String[] args)throws Exception{ Class ClazzProxy=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class); //获得代理类的字节码 System.out.println(ClazzProxy); Constructor[] cons=ClazzProxy.getConstructors(); //获得代理类的构造方法 StringBuilder sb=new StringBuilder(); for(Constructor con:cons){ System.out.print(con.getName()); sb.append('('); Class[] cls=con.getParameterTypes(); for(Class cl:cls){ sb.append(cl.getName()+','); } if(cls!=null&&cls.length!=0) sb.deleteCharAt(sb.length()-1); sb.append(')'); } System.out.println(sb.toString()); System.out.println("Method----------------------------------------"); Method[] meths=ClazzProxy.getMethods(); //获得代理类的成员方法 for(Method meth:meths){ StringBuilder sb1=new StringBuilder(); System.out.print(meth.getName()); sb1.append('('); Class[] cls=meth.getParameterTypes(); for(Class cl:cls){ sb1.append(cl.getName()+','); } if(cls!=null&&cls.length!=0) sb1.deleteCharAt(sb1.length()-1); sb1.append(')'); System.out.println(sb1.toString()); } System.out.println("----------------------------------------------"); Constructor con=ClazzProxy.getConstructor(InvocationHandler.class); //获得构造方法 class Ivct implements InvocationHandler{ public void invoke(){} @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { // TODO Auto-generated method stub return null; } } Collection coll=(Collection)con.newInstance(new Ivct()); //coll.size(); //size会调用InvocationHandler的invoke方法,但是上面的invoke方法返回值是null,而 //size需要的是一个int类型的返回值,所以会报错 Collection proxy2=(Collection)Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler(){ ArrayList al=new ArrayList(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj=method.invoke(al, args); System.out.println(obj+"Test.........."); return obj; } }); proxy2.add("zs"); //每次调用add方法时,add就会调用InvocationHandler的invoke方法 proxy2.add("ls"); proxy2.add("ww"); System.out.println(proxy2); System.out.println(proxy2.size()); } }