在学习Spring的过程中,发现要实现AOP,就必须要使用代理模式,经过课下的学习,代理模式还是不算很难的。
简单来说,代理模式就是我们把目标类要完成的事找代理类来完成,举一个很简单的例子,当我们需要买房的时候,我们大部分是找的中介,这里的中介实际就是一个代理,真正做事情是通过中介找到开发商,由开发商完成卖房子的事情,在这其中,中介可以在里面做一些“手脚”。我们用代码来模拟一下这个场景:
需要说明的是代理类必须保存目标类的引用,且必须为私有变量,同时代理类和目标类需要同时实现相同的接口,这样我们就能在代理类中增加我们需要的功能且不改变目标类实现的原有功能。在实例化代理类时,调用构造方法,实例化目标类,完成原有功能。
需要说明的是上述的代理模式使用的静态代理,举例来说,当我们改变了需求,当我们需要旅游的时候,就会找旅游中介,旅游中介需要完成别的事情,这样就需要我们重新创建一个旅游代理类。这样的话我们的XxxProxy代理类会越来越多,因此这就需要我们重构,将静态代理类改为动态代理。
动态代理
1 JDK动态代理
jdk已经为我们提供了动态代理的方案,java.lang.reflect.Proxy是java动态代理机制实现的主类,它提供了一组静态方法为一组接口动态的生成代理类及其对象
该方法用于获取指定代理对象的调用处理器
获取类加载器和一组接口的指定代理类
该方法为指定类加载器,一组接口生成代理对象的实例
所有JDK的动态代理的实现需要实现InvocationHandler接口,该接口只有一个方法invoke
第一个参数是代理类的实例,第二个参数是被调用的方法对象,第三个参数是调用方法需要的参数
其大致顺序为:
(1) 由ClassLoader将目标类的字节码文件装载到虚拟机中并为其定义类对象,Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中,每次生成动态代理类对象时都需要指定一个类装载器对象
(2)通过反射机制获取代理类的构造函数
(3)通过构造函数创建代理类实例,构造函数中将InvocationHandler实现类作为参数传入。
代码模拟:
getProxy方法将目标类的类加载器,接口,当前InvocationHandler实现类作为参数传入生成实例化对象,实例化对象在调用目标类的方法之前完成增加的功能并返回结果。Proxy类的newProxyInstance方法实际上封装了上述(2),(3)的过程。还有就是当代理类实例调用目标类方法时,实际调用的是InvocationHandler实现类的invoke方法,该方法在对目标类方法调用时进行了拦截,并增加了逻辑处理。
从上面看来,JDK动态代理的实现离不开接口,代理类实例化时需要目标类实现的接口数组来作为参数,存在一定局限性,CGLib类库可以帮助我们实现代理类而不需要接口。
CGLib实现动态代理
CGLib基于类来实现动态代理,它的原理是对指定的目标类生成子类,并覆盖目标类的方法实现增强,代码实现:
完成动态代理需要实现MethodInterceptor接口,该接口只有一个方法intercept,由代码可知生成代理类需要Enhancer完成
由此可知Enhancer的实例对象将目标类作为父类。返回代理类实例,因本人才疏学浅,无法更加深入研究。
由intercepte方法的参数MethodProxy可知CGLib支持方法级别的代理,可以理解为 “方法拦截器”。通过该参数调用的是目标类的目标方法,并在前后增加功能实现原有代码的增强。