从动态代理到Spring AOP(上)

一.前言

  虽然平时日常开发很少用到动态代理,但是动态代理在底层框架等有着非常重要的意义。比如Spring AOP使用cglib和JDK动态代理,Hibernate底层使用了javassit和cglib动态代理,Dubbo使用javassist字节码(具体可以看Dubbo SPI)。

  本文主要介绍什么是动态代理及原理,下文将介绍Spring AOP

  我们先思考一个问题:如何统计一个类各个方法的执行时间?可能你心里有好多答案都可以解决问题。

  那么如果是这个项目的多个不同类呢?可能心里也有答案,但是代码改动量不少。那么有什么其他的方法么?

  这时候动态代理就出来了,它可以灵活的在方法、代码点上切入我们想要实现的逻辑。如图示:

二.体验动态代理

2.1 JDK动态代理

  在Java的java.lang.reflect包下提供了Proxy类和InvocationHandler接口,通过使用这两个类可以生成JDK动态代理类或者JDK动态代理对象

  JDK动态代理只能针对实现了接口的类进行拓展,我们还是就上面的问题来结局。所以这里我们先创建一个接口,叫Developer(开发),里面有个方法是develop方法。有RookieDeveloper(新生开发)、PrimaryDeveloper(初级开发)和AdvancedDeveloper(高级开发)实现其接口。类图如下:

  我现在对AdvancedDeveloper进行动态代理,先来看一下AdvancedDeveloper的代码:

  接下来请看如何使用Proxy和InvocationHandler生成动态代理的:

 

  运行结果如下:

2.2 CGLib动态代理

  CGLIB代理的核心是net.sf.cglib.proxy.Enhancer类。我们可以将自定义的net.sf.cglib.proxy.MethodInterceptor实现类来得到强大的代理。代理的所有方法调用都会被分派给net.sf.cglib.proxy.MethodInterceptor的intercept方法。intercept方法然后调用底层对象。

  我们看一下Cglib动态代理的例子,先看下PrimaryDeveloper类:

  再看下CGLib动态代理测试类:

  简而言之,proxy.invoke方法调用的对象不是代理后的子类,proxy.invokeSuper方法调用的对象是代理后的子类(已增强),所以会再走一遍 MyMethodInterceptor的 interceptor方法,如果是个拦截器链条,就会重新在走一次拦截器链;最后看一下执行结果:

三.动态代理原理

3.1 JDK动态代理

  我们首先看一下java.lang.reflect.Proxy#newProxyInstance这个方法:

  这里还有一个关键点,在java.lang.reflect.Proxy.ProxyClassFactory#apply方法里,有一段代码生产对应的class字节码文件:

  简单总结一下上面的代码:

  1. 生成一个实现interfaces所有接口且继承Proxy类的代理类
  2. 使用Proxy(InvocationHandler h)构造一个代理类实例
  3. 传入我们定义的InvocationHandler(例子中是匿名内部类),构造器实例化了代理对象

  最后我们看一下生成的类的代码,我使用的是Bytecode Viewer,github地址:https://github.com/Konloch/bytecode-viewer。我们用debug evaluate获取到代理类的class文件,然后用Bytecode Viewer瞅瞅是啥样子:

  生产类的代码出来啦,继承Proxy类,实现Developer接口,调用所有方法都转换成了实际调用InvocationHandler接口的invoke方法:

3.2 CGLib动态代理

  我们从生成的动态代理类长啥样开始研究。上面的例子,添加System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/miaojiaxing/Downloads");后运行会生成几个.class文件:

  • PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0:CGLib生成的代理类
  • PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210:代理类的FastClass
  • PrimaryDeveloper$$FastClassByCGLIB$$de1a7774:被代理类的FastClass(有点绕口)

  首先用反编译工具查看一下PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0这个类的代码:

 1 package com.mjx.java.proxy;
 2
 3 import java.lang.reflect.Method;
 4 import net.sf.cglib.core.ReflectUtils;
 5 import net.sf.cglib.core.Signature;
 6 import net.sf.cglib.proxy.Callback;
 7 import net.sf.cglib.proxy.Factory;
 8 import net.sf.cglib.proxy.MethodInterceptor;
 9 import net.sf.cglib.proxy.MethodProxy;
10
11 public class PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0 extends PrimaryDeveloper implements Factory {
12    private boolean CGLIB$BOUND;
13    public static Object CGLIB$FACTORY_DATA;
14    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
15    private static final Callback[] CGLIB$STATIC_CALLBACKS;
16    private MethodInterceptor CGLIB$CALLBACK_0;
17    private static Object CGLIB$CALLBACK_FILTER;
18
19    // 代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应
20    private static final Method CGLIB$laugh$0$Method;
21    private static final MethodProxy CGLIB$laugh$0$Proxy;
22    private static final Object[] CGLIB$emptyArgs;
23
24    // 代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应
25    private static final Method CGLIB$develop$1$Method;
26    private static final MethodProxy CGLIB$develop$1$Proxy;
27
28    private static final Method CGLIB$say$2$Method;
29    private static final MethodProxy CGLIB$say$2$Proxy;
30    private static final Method CGLIB$equals$3$Method;
31    private static final MethodProxy CGLIB$equals$3$Proxy;
32    private static final Method CGLIB$toString$4$Method;
33    private static final MethodProxy CGLIB$toString$4$Proxy;
34    private static final Method CGLIB$hashCode$5$Method;
35    private static final MethodProxy CGLIB$hashCode$5$Proxy;
36    private static final Method CGLIB$clone$6$Method;
37    private static final MethodProxy CGLIB$clone$6$Proxy;
38
39    static void CGLIB$STATICHOOK1() {
40       CGLIB$THREAD_CALLBACKS = new ThreadLocal();
41       CGLIB$emptyArgs = new Object[0];
42       Class var0 = Class.forName("com.mjx.java.proxy.PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0");
43       Class var1;
44       Method[] var10000 = ReflectUtils.findMethods(new String[]{"laugh", "()V", "develop", "()V", "say", "()V"}, (var1 = Class.forName("com.mjx.java.proxy.PrimaryDeveloper")).getDeclaredMethods());
45       CGLIB$laugh$0$Method = var10000[0];
46       CGLIB$laugh$0$Proxy = MethodProxy.create(var1, var0, "()V", "laugh", "CGLIB$laugh$0");
47       CGLIB$develop$1$Method = var10000[1];
48       CGLIB$develop$1$Proxy = MethodProxy.create(var1, var0, "()V", "develop", "CGLIB$develop$1");
49       CGLIB$say$2$Method = var10000[2];
50       CGLIB$say$2$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$2");
51       var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
52       CGLIB$equals$3$Method = var10000[0];
53       CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
54       CGLIB$toString$4$Method = var10000[1];
55       CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
56       CGLIB$hashCode$5$Method = var10000[2];
57       CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
58       CGLIB$clone$6$Method = var10000[3];
59       CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
60    }
61
62    final void CGLIB$laugh$0() {
63       super.laugh();
64    }
65
66    public final void laugh() {
67       MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
68       if (var10000 == null) {
69          CGLIB$BIND_CALLBACKS(this);
70          var10000 = this.CGLIB$CALLBACK_0;
71       }
72
73       if (var10000 != null) {
74          // 拦截器
75          var10000.intercept(this, CGLIB$laugh$0$Method, CGLIB$emptyArgs, CGLIB$laugh$0$Proxy);
76       } else {
77          super.laugh();
78       }
79    }
80
81    // methodProxy.invokeSuper会调用
82    final void CGLIB$develop$1() throws Exception {
83       super.develop();
84    }
85
86    public final void develop() throws Exception {
87       MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
88       if (var10000 == null) {
89          CGLIB$BIND_CALLBACKS(this);
90          var10000 = this.CGLIB$CALLBACK_0;
91       }
92
93       // 拦截
94       if (var10000 != null) {
95          var10000.intercept(this, CGLIB$develop$1$Method, CGLIB$emptyArgs, CGLIB$develop$1$Proxy);
96       } else {
97          super.develop();
98       }
99    }

  我们可以看到代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应,这个非常关键,上面已经用红色标注。

3.2.1 MethodProxy

  CGLIB$develop$1$Proxy = MethodProxy.create(var1, var0, "()V", "develop", "CGLIB$develop$1");

  我们先看下创建MethodProxy:

 1 public class MethodProxy {
 2     private Signature sig1;
 3     private Signature sig2;
 4     private CreateInfo createInfo;
 5
 6     private final Object initLock = new Object();
 7     private volatile FastClassInfo fastClassInfo;
 8
 9     /**
10      * For internal use by {@link Enhancer} only; see the {@link net.sf.cglib.reflect.FastMethod} class
11      * for similar functionality.
12      */
13     // c1:被代理对象Class
14     // c2:代理对象Class
15     // desc:入参类型
16     // name1:被代理方法名
17     // name2:代理方法名
18     public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
19         MethodProxy proxy = new MethodProxy();
20         proxy.sig1 = new Signature(name1, desc);
21         proxy.sig2 = new Signature(name2, desc);
22         proxy.createInfo = new CreateInfo(c1, c2);
23         return proxy;
24     }
25
26     private static class CreateInfo
27     {
28         Class c1;
29         Class c2;
30         NamingPolicy namingPolicy;
31         GeneratorStrategy strategy;
32         boolean attemptLoad;
33
34         public CreateInfo(Class c1, Class c2)
35         {
36             this.c1 = c1;
37             this.c2 = c2;
38             AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
39             if (fromEnhancer != null) {
40                 namingPolicy = fromEnhancer.getNamingPolicy();
41                 strategy = fromEnhancer.getStrategy();
42                 attemptLoad = fromEnhancer.getAttemptLoad();
43             }
44         }
45     }
46 }

  创建代理之后,执行方法这里走到红色的代码:var10000.intercept(this, CGLIB$develop$1$Method, CGLIB$emptyArgs, CGLIB$develop$1$Proxy);走到了我们写的CglibInterceptor类的intercept方法,里面调用了proxy.invokeSuper(obj,args);

 1 public Object invokeSuper(Object obj, Object[] args) throws Throwable {
 2     try {
 3         init();
 4         FastClassInfo fci = fastClassInfo;
 5         // 这里的f2就是PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210
 6         return fci.f2.invoke(fci.i2, obj, args);
 7     } catch (InvocationTargetException e) {
 8         throw e.getTargetException();
 9     }
10 }
11
12 private static class FastClassInfo{
13     FastClass f1;//被代理类FastClass,这里就是PrimaryDeveloper$$FastClassByCGLIB$$de1a7774
14     FastClass f2;//代理类FastClass,这里就是PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210
15     int i1;//被代理方法index
16     int i2;//代理方法index
17 } 

3.2.1 FastClass机制  

  Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它为代理类和被代理类各生成一个Class(就是上面的f1和f2),它会为代理类或被代理类的方法分配一个index(int类型)。这个index是用签名的hashCode来计算出来的Index(下面代码有),FastClass就可以直接定位要调用的方法直接进行调用,那么就省去了反射,所以调用效率比JDK动态代理通过反射调用高。f2我们已经知道是PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210,下面我们反编译一下f2看看:

  继续看下f2的invoke方法,直接调用PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0的方法,无需反射。

  最后我们整理一下调用过程,这样比较清晰了吧,如图:

四.总结

JDK动态代理:

  • 首先这个类要实现了某接口
  • 其核心就是克隆interfaces的所有接口,继承Proxy类,生成一个心类作为代理类,这个类里有我们定义的实现InvocationHandler接口的类进行代理逻辑处理

CGLib动态代理:

  • JDK动态代理有个重大缺陷,必须要实现接口才可以使用,而CGLib动态代理只要有个类就行,动态生成子类。如果是private方法,final方法等描述的方法是不能被代理的
  • Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制

原文地址:https://www.cnblogs.com/GrimMjx/p/11194283.html

时间: 2024-10-01 07:01:55

从动态代理到Spring AOP(上)的相关文章

【设计模式】代理模式:静态代理,动态代理,spring aop

代理模式分为静态代理和动态代理.我们拿链家来举例子,我们本人是真实的对象,有真实的业务需求:需要去找房子:链家是中介,是代理类,他来帮我执行找房子的这个操作. 静态代理: 1.实现一个接口 public interface SearchHome { public void search(); } 2.构建实现接口的委托类 public class Master implements SearchHome { @Override public void search() { System.out.

反射实现AOP动态代理模式(Spring AOP实现原理)

其实AOP的意思就是面向切面编程. OO注重的是我们解决问题的方法(封装成Method),而AOP注重的是许多解决问题的方法中的共同点,是对OO思想的一种补充! 还是拿人家经常举的一个例子讲解一下吧: 比如说,我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,或部分监控.也许我们就会在要一些方法前去加上一条日志记录. 我们写个例子看看我们最简单的解决方案 我们先写一个接口IHello.java代码如下: package sinosoft.dj.aop.st

动态代理实现Spring Aop

引言 我们在前两篇文章中,都为这篇做了铺垫,我们现在来做这样一件事情,在业务逻辑中添加Aop的非业务逻辑. AopClinetTest: package com.tgb.client; import com.tgb.config.BeanFactory; import com.tgb.config.ClassPathXmlApplicationContext; import com.tgb.dao.UserDao; import com.tgb.domain.User; /** * AOP效果测

AOP、静态代理、JDK动态代理、CGLIB动态代理、Spring实现AOP、IOC+AOP

一.为什么需要代理模式 假设需实现一个计算的类Math.完成加.减.乘.除功能,如下所示: 1 package com.zhangguo.Spring041.aop01; 2 3 public class Math { 4 //加 5 public int add(int n1,int n2){ 6 int result=n1+n2; 7 System.out.println(n1+"+"+n2+"="+result); 8 return result; 9 } 1

jdk动态代理与cglib代理、spring aop代理实现原理解析

原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 动态代理实现主要有2种形式,主要分为: 1.jdk动态代理: 1)原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下) 2)实现方式: 1. 通过实现Invocati

动态代理的应用---AOP

1. AOP:面向切面编程 2. 实现原理:动态代理 3. 配置方式: <1> @AspectJ <2> XML 推荐<2>,原因:可实现"热插拔". 4. 相关术语介绍 <1> 切面:aspect 需要实现的交叉功能----通知和切入点的结合. 通知和切入点共同定义了关于前面的全部功能:它的功能.在何时和何地完成功能. <2> 通知: advice 定义了切面是什么以及何时使用. 描述的内容:切面需要完成的工作.何时执行该工

不用Unity库,利用.NET动态代理自己实现AOP

AOP意为面向切面的程序设计,主要表现为对不同的代码逻辑进行隔离,从而降低不同业务逻辑之间的耦合性,AOP又理解为“横切”,可以在不改变原有实现的情况下,对代码进行拦截和扩展,如果原有设计像一个瓶子,AOP就相当于一个贴纸,是贴在瓶子外面的,而不是打开瓶盖从瓶口把实现放进瓶子里. .NET中实现AOP的第三方库有很多,这里不再阐述了,在这里我们主要用到了.NET中的动态代理技术,为了让大家更深入地理解,这里借用一下上一篇文章(不用Unity库,自己实现.NET轻量级依赖注入)中的例子,在上一篇文

Java代理及Spring AOP

Spring的AOP核心采用的设计模式采用的是代理模式,先介绍下Java的代理. 这里借鉴一下其他人的介绍,https://blog.csdn.net/fighterandknight/article/details/51200470 一 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身

动态代理在Spring中的应用

Spring中主要使用cglib和jdk动态代理,主要在SpringAop中有大量应用. JDK动态代理 jdk动态代理主要使用场景是被代理的对象有实现的接口.最终生成的代理类: class $Proxy0 extends Proxy implements IDao jdk动态代理主要是基于反射,其实我们完全可以自己模拟:其中两个比较关键的思路: 使用反射解析目标对象的属性.方法等 根据解析的内容生成proxy.class,说白了就是把要生成的class按照字符串的形式拼接,最终通过ClassL