Spring 源码梳理(九) AOP与动态代理
来源:互联网 发布:音乐广告合成软件 编辑:程序博客网 时间:2024/05/05 22:27
Spring 源码梳理(九) AOP与动态代理
SpringAOP的核心是动态代理,就从动态代理开始分析,然后就会谈到静态代理,以及为什么会有代理这个说法,后面逐一分析:
1.什么是代理,为什么会有代理?
类甲的代理就是代替甲来完成甲的功能,比如实际上就是有点像房产中介,本身不提供房产,但是可以帮助开发商把房子卖出去,并且在卖的过程中还能做一些操作,赚的利益。那为什么会有代理呢? 就类甲来说,我们想执行这个类甲的相关方法,但是在执行的前后想做一些其他的操作,由此就想到了一个方法,就是重写一个类乙,把甲的方法都重写一遍,还加上了自己的东西,这就是静态代理。
2.静态代理,如下图:
上面是典型的静态的代理模式,我们看一下示例代码:
Subject
package day_20161010;public interface Subject { public int add(int a,int b); public int div(int a,int b);}RealSubject
package day_20161010;public class RealSubject implements Subject{public int add(int a, int b) {return a+b;}public int div(int a, int b) {return a/b;}}Proxy
package day_20161010;public class Proxy implements Subject{private Subject subject;public Proxy(Subject subject){this.subject = subject;}public int add(int a, int b) {System.out.println("add before...");return subject.add(a, b);}public int div(int a, int b) {System.out.println("div before...");return subject.div(a, b);}}Main
package day_20161010;public class Main {public static void main(String[] args) {RealSubject realSubject = new RealSubject();Subject subject = new Proxy(realSubject);subject.add(1, 2);subject.div(3, 1);}}代码很清晰,只是这样的一个代理是一个java类,1)需要重写每一个接口的方法 2)如果我们增加或减少实现的方法,在代理类中也要增加或减少 3)当代理类想改变对每个实现类方法前后的操作时,则要对所有的代理方法进行更改,如何避免这三种情况,下面进行分析。
3.先看一张图,图的流程已经在这一篇文章中讲述过http://blog.csdn.net/Jintao_Ma/article/details/52710960,本篇只看其中的红色部分:
我们看到运行期系统有这样一种能力,它能够在运行期间,根据.class的组织结构动态的生成.class文件的字节码,然后再由类加载器进行加载。由于这个能力,我们可以不再使用静态代理,而让代理class类在运行期期间动态生成。【PS:不知道是为了实现动态代理而让运行期系统具备这种能力,还是运行期先有这种能力从而正好实现动态代理,这点就先不用管了】
如何利用这种能力,对类进行动态代理呢?
1)使用ASM和Javassist框架来手动在运行期系统中动态的生成代理class文件。但是使用起来手动创建了很多的业务代码,同样增加了复杂度
2)JDK本身给我们提供了一种较好的实现,如下:
InvocationHandler角色的由来
仔细思考代理模式中的代理Proxy角色。Proxy角色在执行代理业务的时候,无非是在调用真正业务之前或者之后做一些“额外”业务,如图:
由上图可以看出,代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。换一种思路就是:在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。那么,为了构造出具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发的管理器,让这个管理器统一地管理触发。这种管理器就是Invocation Handler。
动态代理模式的结构跟上面的静态代理模式稍微有所不同,多引入了一个InvocationHandler角色,它的作用如下:
在静态代理中,代理Proxy中的方法,都指定了调用了特定的realSubject中的对应的方法,在上面的静态代理模式下,Proxy所做的事情,无非是调用在不同的request时,调用触发realSubject对应的方法,动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:
package day_20160928;public class mathCount implements Count{ public int add(int a,int b){ return a+b; } public int div(int a,int b){ return a/b; } }
4.前面一片文章分析过动态代理,但是不够深刻http://blog.csdn.net/Jintao_Ma/article/details/51107096,下面做一个深刻的实例,代码中的注释有详细的解释:
Count
package day_20160928;public interface Count { public int add(int a,int b); public int div(int a,int b); public static int a = 1;}mathCount
package day_20160928;public class mathCount implements Count{ public int add(int a,int b){ return a+b; } public int div(int a,int b){ return a/b; } }dyProxy
package day_20160928;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class dyProxy implements InvocationHandler{ private Object subject; public dyProxy(Object subject){ this.subject = subject; } /** * 当代理对象调用真实对象的方法时,会自动跳转到其关联的handler对象的invoke方法, * 就是下面的这个方法 * @param proxy 就是我们调用哪一个代理类,就会把哪一个代理类传进来,就是Main中的count * @param method args 在使用代理类的方法时会传进来方法名和参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { int b = ((Count)proxy).a; System.out.println("dyProxy.invoke():"+b); /*在执行代理对象的方法前,添加自己的操作*/ System.out.println("before method:"); System.out.println("the method:"+method); try { /*以反射的方式调用真实对象中的方法*/ method.invoke(subject, args); } catch (Exception e) { e.printStackTrace(); /*出现异常打印信息*/ System.out.println("exception:"); } /*在执行代理对象的方法后,添加自己的操作*/ System.out.println("after method:"); return 1; } }Main
package day_20160928;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { /*建立要代理的真实对象*/ mathCount realcount = new mathCount(); /*我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的*/ InvocationHandler invocationHandler = new dyProxy(realcount); /**通过Proxy的newProxyInstance来创建我们的代理对象 * @param handler.getClass().getClassLoader(), 这里就是在运行期期间动态地生成二进制字节码, * 然后重新用类加载器进行加载(就是本篇文章第二张图中讲的动态生成类然后用类加载器加载)。 * @param realSubject.getClass().getInterfaces(),标识把这些接口与触发器invocationHandler关联起来, * 以后代理类执行到这些接口时,就会调用invocationHandler的invoke方法;同时,realcount.getClass(). * getInterfaces()表示要代理的类必须有实现的接口 * @param handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上 * @return count * 表示生成的代理类也只能代理接口中的方法,如Count count = (Count) */ Count count = (Count)Proxy.newProxyInstance(realcount.getClass().getClassLoader(), realcount.getClass().getInterfaces(), invocationHandler); count.add(1, 2); count.div(3, 1); } }我们分析下动态代理是如何解决静态代理的那三个问题的:
1)首先不需要重写接口的每一个方法,使用method.invoke(subject, args)一句就可以了
2)我们在接口中和实现类中增加方法,在代理类中还是method.invoke(subject, args)就够了
3)在方法的前后进行更改时,也只需要在method.invoke(subject, args)的前后进行更改可以了。
有人说,静态代理当有很多的实现类的时候会产生多个代理类,很明显不对,我们看上面静态代理例子中的Proxy,构造方法可以接受任何实现类,而只需要Proxy就可以了。所以这并不是动态代理相对于静态代理的优点,真正的优点是上面的三点。用一张图来描述:
咦?怎么看起来那么像AOP啊。没错,这就是AOP所宣扬的那样面向切面, AOP本来就是依据动态代理来的。
5.所谓代理,就是要保证代理类和被代理类具有相同的功能;那怎么保证?
一种是继承同一个的接口,就像上面JDK实现中的动态代理,它使用的是接口的形式; 我们也发现了一个问题,上面JDK动态代理这样一段代码:
Count count = (Count)Proxy.newProxyInstance(realcount.getClass().getClassLoader(), realcount.getClass().getInterfaces(), invocationHandler);我们看到realcount.getClass(),这段代码实际上我们是把实现类中的接口信息与触发器invocationHandler绑定,也就是说如果实现类有接口中没有的方法是不能被代理的。总的来说,JDK这种代理1)要保证有接口,2)不能代理接口中没有的方法
为避免上面的两种缺陷,这就是要说的另一种动态代理实现方式,那就是代理类继承被代理类,不需要接口,这种实现如 Cglib。 实例如下:
realSubject
package day_20161011;public class realSubject {public void add() { System.out.println("realSubject.add()"); } }doProxy
package day_20161011;import java.lang.reflect.Method;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy; /* * 实现了方法拦截器接口 */ public class doProxy implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before method"); proxy.invokeSuper(obj, args); System.out.println("after method"); return null; } }Main
package day_20161011;import org.springframework.cglib.proxy.Enhancer;public class Main {public static void main(String[] args) {realSubject realsubject = new realSubject(); doProxy doproxy = new doProxy(); //cglib 中加强器,用来创建动态代理 Enhancer enhancer = new Enhancer(); //设置要创建动态代理的类 enhancer.setSuperclass(realsubject.getClass()); //设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截 enhancer.setCallback(doproxy); realSubject proxy =(realSubject)enhancer.create(); proxy.add(); }}它的流程是这样的:
1).查找A上的所有非final 的public类型的方法定义;
2).将这些方法的定义转换成字节码;
3).将组成的字节码转换成相应的代理的class对象;
4).实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
6.本篇总结:其实所有AOP带来的好处都是基于一点------"在被代理的方法前后执行一些操作"(^_^)
本文参考文章:http://blog.csdn.net/luanlouis/article/details/24589193
- Spring 源码梳理(九) AOP与动态代理
- 动态代理与spring AOP
- Spring AOP与动态代理
- Spring AOP 静态代理与动态代理
- spring源码-5-aop动态代理
- Spring AOP与Java动态代理
- Spring - 动态代理 与 AOP 理解
- JDK动态代理与Spring AOP
- (九)Spring核心框架 - AOP之动态代理机制
- Spring Aop 动态代理
- Spring AOP动态代理
- Spring AOP 动态代理
- spring aop动态代理
- Spring(AOP动态代理)
- Spring AOP中的JDK动态代理与CGLIB代理
- 动态代理与AOP
- 动态代理与AOP
- 动态代理与AOP
- HTTPS协议
- jsonp跨域请求
- 轻松制作 macOS Sierra 正式版 USB 安装盘!
- 关于angularJS radio的使用
- 理解JSBridge
- Spring 源码梳理(九) AOP与动态代理
- 机器学习笔记
- 【网络】制作双绞线
- Linux 命令集锦
- lightoj 1023 水dfs
- maven项目在myeclipse中不出现Maven Dependencies 和maven标识的解决方法
- Mybatis - #{}及${}传值的区别
- openssl生成ssl证书(搭建HTTPS服务器)
- RabbitMQ 实战教程(五) 主题