Spring AOP高级——源码实现(1)动态代理技术
2017-11-09 23:12
507 查看
jdk1.8.0_144
在正式进入Spring AOP的源码实现前,我们需要准备一定的基础也就是面向切面编程的核心——动态代理。 动态代理实际上也是一种结构型的设计模式,JDK中已经为我们准备好了这种设计模式,不过这种JDK为我们提供的动态代理有2个缺点:
只能代理实现了接口的目标对象;
基于反射,效率低
鉴于以上2个缺点,于是就出现了第二种动态代理技术——CGLIB(Code Generation Library)。这种代理技术一是不需要目标对象实现接口(这大大扩展了使用范围),二是它是基于字节码实现(这比反射效率高)。当然它并不是完全没有缺点,因为它不能代理final方法(因为它的动态代理实际是生成目标对象的子类)。
Spring AOP中生成代理对象时既可以使用JDK的动态代理技术,也可以使用CGLIB的动态代理技术,本章首先对这两者动态代理技术做简要了解,便于后续源码的理解。
接下来就是我们需要代理的真实对象,即目标对象:
这是一个真实的对象,我们希望在不更改原有代码逻辑的基础上增强该类的sayHello方法,利用JDK动态代理技术需要我们实现InvocationHandler接口中的invoke方法:
第15行,在invoke方法中可以看到,在调用目标对象的方法前后我们对方法进行了增加,这其实就是AOP中Before和After通知的奥义所在。
加入测试代码:
执行结果:
View Code
前面提到了CGLib动态代理技术不需要目标对象实现自一个接口:
下面我们就使用CGLib代理这个类:
可以看到同样是需要实现一个接口——MethodIntercept,并且实现一个和invoke类似的方法——intercept。
加入测试代码:
执行结果:
可以看到的执行结果和JDK动态代理的结果一样,不同的是代理类的类型是cglibproxy.RealSubject$$EnhancerByCGLIB$$cb568e93。接着我们来看CGLib是如何生成代理类的。
生成代理类的是ProxySubject类中的getProxy方法,而其中又是传入两个参数:
参数设置好后就调用enhancer.create()方法创建代理类。
看来还在调用一个叫createHelper的方法。
接着查看createHelper的剩余代码:
马马虎虎地只能说是介绍了JDK与CGLib两种动态代理技术,并没有很深入地研究,特别是在两者在缓存机制上的实现,略感遗憾。
另外,在开头提到了CGLib的性能比JDK高,这实际上并不准确。或许这在特别条件下的确如此,因为在我实测发现JDK8的动态代理效率非常高,甚至略高于CGLib,但是在JDK6的环境下的效率就显得比较低了。所以,通常所说的CGLib性能比JDK动态代理要高,是传统的挂念,实际上Java一直都在不断优化动态代理性能,在比较高版本的JDK条件下可以放行大胆的使用JDK原生的动态代理。
这是一个能给程序员加buff的公众号
在正式进入Spring AOP的源码实现前,我们需要准备一定的基础也就是面向切面编程的核心——动态代理。 动态代理实际上也是一种结构型的设计模式,JDK中已经为我们准备好了这种设计模式,不过这种JDK为我们提供的动态代理有2个缺点:
只能代理实现了接口的目标对象;
基于反射,效率低
鉴于以上2个缺点,于是就出现了第二种动态代理技术——CGLIB(Code Generation Library)。这种代理技术一是不需要目标对象实现接口(这大大扩展了使用范围),二是它是基于字节码实现(这比反射效率高)。当然它并不是完全没有缺点,因为它不能代理final方法(因为它的动态代理实际是生成目标对象的子类)。
Spring AOP中生成代理对象时既可以使用JDK的动态代理技术,也可以使用CGLIB的动态代理技术,本章首先对这两者动态代理技术做简要了解,便于后续源码的理解。
JDK动态代理技术
JDK动态代理技术首先要求我们目标对象需要实现一个接口:package proxy; /** * Created by Kevin on 2017/11/8. */ public interface Subject { void sayHello(); }
接下来就是我们需要代理的真实对象,即目标对象:
package proxy; /** * 目标对象,即需要被代理的对象 * Created by Kevin on 2017/11/8. */ public class RealSubject implements Subject{ public void sayHello() { System.out.println("hello world"); } }
这是一个真实的对象,我们希望在不更改原有代码逻辑的基础上增强该类的sayHello方法,利用JDK动态代理技术需要我们实现InvocationHandler接口中的invoke方法:
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ProxySubject implements InvocationHandler { private Object target; public ProxySubject(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用前"); Object object = method.invoke(target, args); System.out.println("调用后"); return object; } }
第15行,在invoke方法中可以看到,在调用目标对象的方法前后我们对方法进行了增加,这其实就是AOP中Before和After通知的奥义所在。
加入测试代码:
package proxy; import java.lang.reflect.Proxy; /** * Created by Kevin on 2017/11/8. */ public class Test { public static void main(String[] args) { Subject subject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), new ProxySubject(new RealSubject())); subject.sayHello(); //查看subject对象的类型 System.out.println(subject.getClass().getName()); } }
执行结果:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency>
View Code
前面提到了CGLib动态代理技术不需要目标对象实现自一个接口:
package cglibproxy; /** * 目标对象(需要被代理的类) * Created by Kevin on 2017/11/6. */ public class RealSubject { public void sayHello() { System.out.println("hello"); } }
下面我们就使用CGLib代理这个类:
package cglibproxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 代理类 * Created by Kevin on 2017/11/6. */ public class ProxySubject implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz) { enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); //用于创建无参的目标对象代理类,对于有参构造器则调用Enhancer.create(Class[] argumentTypes, Object[] arguments),第一个参数表示参数类型,第二个参数表示参数的值。 } @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("调用前"); Object result = methodProxy.invokeSuper(object, args); System.out.println("调用后"); return result; } }
可以看到同样是需要实现一个接口——MethodIntercept,并且实现一个和invoke类似的方法——intercept。
加入测试代码:
package cglibproxy; /** * Created by Kevin on 2017/11/6. */ public class Main { public static void main(String[] args) { RealSubject subject = (RealSubject) new ProxySubject().getProxy(RealSubject.class); subject.sayHello(); System.out.println(subject.getClass().getName()); } }
执行结果:
可以看到的执行结果和JDK动态代理的结果一样,不同的是代理类的类型是cglibproxy.RealSubject$$EnhancerByCGLIB$$cb568e93。接着我们来看CGLib是如何生成代理类的。
生成代理类的是ProxySubject类中的getProxy方法,而其中又是传入两个参数:
enhancer.setSuperclass(clazz); //设置需要代理的类 enhancer.setCallback(this); //设置回调方法
参数设置好后就调用enhancer.create()方法创建代理类。
public Object create() { classOnly = false; //这个字段设置为false表示返回的是具体的Object代理类,在createClass()方法中设置的是classOnly=true表示的返回class类型的代理类。 argumentTypes = null; //创建的是无参目标对象的代理类,故没有参数,所以参数类型设置为null return createHelper(); }
看来还在调用一个叫createHelper的方法。
private Object createHelper() { preValidate(); //提前作一些校验 // …… }
private void preValidate() { if (callbackTypes == null) { callbackTypes = CallbackInfo.determineTypes(callbacks, false); validateCallbackTypes = true; } //检查回调方法是否为空 if (filter == null) { //检查是否设置过滤器,如果设置了多个回调方法就需要设置过滤器 if (callbackTypes.length > 1) { throw new IllegalStateException("Multiple callback types possible but no filter specified"); } filter = ALL_ZERO; } }
接着查看createHelper的剩余代码:
private Object createHelper() { preValidate(); Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null, ReflectUtils.getNames(interfaces), filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter), callbackTypes, useFactory, interceptDuringConstruction, serialVersionUID); this.currentKey = key; //在CGLib中也使用到了缓存机制,这段代码也比较复杂,有关缓存的策略暂时也不做分析吧 Object result = super.create(key); //利用字节实现并创建代理类对象 return result; }
马马虎虎地只能说是介绍了JDK与CGLib两种动态代理技术,并没有很深入地研究,特别是在两者在缓存机制上的实现,略感遗憾。
另外,在开头提到了CGLib的性能比JDK高,这实际上并不准确。或许这在特别条件下的确如此,因为在我实测发现JDK8的动态代理效率非常高,甚至略高于CGLib,但是在JDK6的环境下的效率就显得比较低了。所以,通常所说的CGLib性能比JDK动态代理要高,是传统的挂念,实际上Java一直都在不断优化动态代理性能,在比较高版本的JDK条件下可以放行大胆的使用JDK原生的动态代理。
这是一个能给程序员加buff的公众号
相关文章推荐
- Spring AOP的底层实现技术---JDK动态代理
- Spring AOP的底层实现技术---JDK动态代理
- Spring AOP的底层实现技术---JDK动态代理
- Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程
- Spring AOP的底层实现技术---JDK动态代理
- Spring AOP的底层实现技术---JDK动态代理
- spring AOP的底层实现技术---JDK动态代理
- Spring AOP的底层实现技术---CGLib动态代理
- Spring AOP的底层实现技术---JDK动态代理
- 基于jdk动态代理的实现与源码解析
- Spring AOP 和 动态代理技术
- Spring AOP的底层实现技术---JDK动态代理
- JAVA 动态代理(proxy)的实现和源码分析
- 使用Proxy反射类实现AOP动态代理技术
- spring aop 手动实现简单的动态代理
- 使用Java动态代理技术实现AOP
- 动态代理实现Spring Aop
- Spring AOP动态代理原理与实现方式
- 使用JDK中的Proxy技术实现AOP功能[动态代理]
- Spring AOP技术--动态代理