分析代理类的作用与原理及AOP概念

来源:互联网 发布:python strip函数内容 编辑:程序博客网 时间:2024/04/29 22:32

   程序中的代理 


要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法

的运行时间、事务管理、等等。



编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码 。


如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则 配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易 。







安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务重要原则,不要把供货商暴露给你的客户。



      动态代理技术



1.要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态模式代理方式,将是一件

非常麻烦的事情!写成百上千个代理类,是不是太累!


2.JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。


    3.JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。


4.CGLIB库可以动态生成一个类的子类 ,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现 

接口的类生成动态代理类,那么可以使用CGLIB库。

5.代理类的各个方法中通除了要调用目标的相应方法和对外返回 目标返回的结果外,还可能在代理 方法中的如下四个位置加上系统功能代码:

1)在调用目标方法前。

2)  在调用目标方法后。

3)在调用目标方法前后。

4)在处理目标方法异常的catch块中。



分析JVM动态生成的类


创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass()方法各个参数。

编码列出动态类中的所有构造方法和参数签名

编码列出动态类中的所有方法和参数签名

创建动态类的实例对象

用反射获得构造方法

编写一个最简单的InvocationHandler类的实例对象传进去 

打印创建的对象和调用对象的没有返回傎方法和getClass方法,演示调用其他有返回值的方法报告了异常

将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯内部类


动态生成的类实现 了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有 方法和一个如下接受InvocationHandler参数的构造方法。

构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该 方法内部的代码会是怎么样的呢?



public interface Advice {void beforeMethod(Method method);void afterMethod(Method method);}



public class MyAdvice implements Advice {long beginTime = 0;public void afterMethod(Method method) {// TODO Auto-generated method stubSystem.out.println("从传智播客毕业上班啦!");long endTime = System.currentTimeMillis();System.out.println(method.getName() + " running time of " + (endTime - beginTime));}public void beforeMethod(Method method) {// TODO Auto-generated method stubSystem.out.println("到传智播客来学习啦!");beginTime = System.currentTimeMillis();}}

private static Object getProxy(final Object target,final Advice advice) {Object proxy3 = Proxy.newProxyInstance(target.getClass().getClassLoader(),/*new Class[]{Collection.class},*/target.getClass().getInterfaces(),new InvocationHandler(){public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {/*long beginTime = System.currentTimeMillis();Object retVal = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println(method.getName() + " running time of " + (endTime - beginTime));return retVal;*/advice.beforeMethod(method);Object retVal = method.invoke(target, args);advice.afterMethod(method);return retVal;}});return proxy3;}

final ArrayList target = new ArrayList();Collection proxy3 = (Collection)getProxy(target,new MyAdvice());proxy3.add("zxx");proxy3.add("lhm");proxy3.add("bxd");System.out.println(proxy3.size());System.out.println(proxy3.getClass().getName());



package cn.itcast.day3;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 {/** * @param args */public static void main(String[] args) throws Exception{// TODO Auto-generated method stubClass clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);System.out.println(clazzProxy1.getName());System.out.println("----------begin constructors list----------");/*$Proxy0()$Proxy0(InvocationHandler,int)*///clazzProxy1这个类中全有什么构造方法Constructor[] constructors = clazzProxy1.getConstructors();for(Constructor constructor : constructors){String name = constructor.getName();StringBuilder sBuilder = new StringBuilder(name);sBuilder.append('(');Class[] clazzParams = constructor.getParameterTypes();for(Class clazzParam : clazzParams){sBuilder.append(clazzParam.getName()).append(',');}if(clazzParams!=null && clazzParams.length != 0)sBuilder.deleteCharAt(sBuilder.length()-1);sBuilder.append(')');System.out.println(sBuilder.toString());}System.out.println("----------begin methods list----------");/*$Proxy0()$Proxy0(InvocationHandler,int)*/Method[] methods = clazzProxy1.getMethods();for(Method method : methods){String name = method.getName();StringBuilder sBuilder = new StringBuilder(name);sBuilder.append('(');Class[] clazzParams = method.getParameterTypes();for(Class clazzParam : clazzParams){sBuilder.append(clazzParam.getName()).append(',');}if(clazzParams!=null && clazzParams.length != 0)sBuilder.deleteCharAt(sBuilder.length()-1);sBuilder.append(')');System.out.println(sBuilder.toString());}System.out.println("----------begin create instance object----------");//Object obj = clazzProxy1.newInstance();Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);class MyInvocationHander1 implements InvocationHandler{public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stubreturn null;}}Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());System.out.println(proxy1);proxy1.clear();//proxy1.size();//System.out.println("111111111111111");Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {return null;}});final ArrayList target = new ArrayList();Collection proxy3 = (Collection)getProxy(target,new MyAdvice());proxy3.add("zxx");proxy3.add("lhm");proxy3.add("bxd");System.out.println(proxy3.size());System.out.println(proxy3.getClass().getName());}private static Object getProxy(final Object target,final Advice advice) {Object proxy3 = Proxy.newProxyInstance(target.getClass().getClassLoader(),/*new Class[]{Collection.class},*/target.getClass().getInterfaces(),new InvocationHandler(){public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {/*long beginTime = System.currentTimeMillis();Object retVal = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println(method.getName() + " running time of " + (endTime - beginTime));return retVal;*/advice.beforeMethod(method);Object retVal = method.invoke(target, args);advice.afterMethod(method);return retVal;}});return proxy3;}}


总结思考:让JVM创建动态类及其实例对象,需要给它提供那些信息。

三个方面:

1)生成的类中有那些方法,通过让其实现哪些接口的方式进行告知;

2)产生的类字节码必须个一个关联的类加载器对象;

3)生成的类中的访求 的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的Invoke()方法中加一点代码,就可以看到这些代码被调用运行了。

用Proxy.newInstance方法直接一步创建出代理对象。








0 0
原创粉丝点击