封面画师:T5-茨舞(微博) 封面ID:97451569
参考视频:黑马程序员Spring视频教程,全面深度讲解spring5底层原理
源码仓库:mofan212/advanced-spring (github.com)
9. AspectJ 编译器增强
创建一个 SpringBoot 项目,除了常见的依赖外,记得导入 AOP 相关的依赖:
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-aop</artifactId > </dependency >
一个 Service 类:
1 2 3 4 5 6 7 8 9 10 11 @Service public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); public void foo () { log.info("foo()" ); } }
一个切面类,注意这个切面类没有被 Spring 管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Aspect public class MyAspect { private static final Logger log = LoggerFactory.getLogger(MyAspect.class); @Before("execution(* indi.mofan.service.MyService.foo())") public void before () { log.info("before()" ); } }
一个用于测试的主启动类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication public class A10Application { private static final Logger log = LoggerFactory.getLogger(A10Application.class); public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A10Application.class, args); MyService service = context.getBean(MyService.class); log.info("service class: {}" , service.getClass()); service.foo(); context.close(); } }
运行主启动类后,控制台会显示:
indi.mofan.A10Application : service class: class indi.mofan.service.MyService
indi.mofan.aop.MyAspect : before()
indi.mofan.service.MyService : foo()
如果完全按照上述步骤进行,会发现 输出结果和给出的结果不一样。
在揭晓答案前,查看 service.getClass()
打印出的信息,它打印出的是原始类的 Class 信息,而非代理类的 Class 信息。
如果要问到 Spring AOP 的实现原理是什么,一下就能想到的是使用了代理,但这里并没有使用代理,依旧实现了增强。
这是因为在 pom.xml 中还引入了一个插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <build > <plugins > <plugin > <groupId > org.codehaus.mojo</groupId > <artifactId > aspectj-maven-plugin</artifactId > <version > 1.11</version > <configuration > <complianceLevel > 1.8</complianceLevel > <source > 8</source > <target > 8</target > <showWeaveInfo > true</showWeaveInfo > <verbose > true</verbose > <Xlint > ignore</Xlint > <encoding > UTF-8</encoding > </configuration > <executions > <execution > <goals > <goal > compile</goal > <goal > test-compile</goal > </goals > </execution > </executions > </plugin > </plugins > </build >
之后不在使用 IDEA 自带的编译器进行编译,而是使用 Maven 编译,即:
编译之后查看生成的 target
文件夹下的 MyService.class
文件:
1 2 3 4 5 6 7 8 9 10 11 12 @Service public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); public MyService () { } public void foo () { MyAspect.aspectOf().before(); log.info("foo()" ); } }
可以看到在 foo()
方法中增加了一行代码:MyAspect.aspectOf().before();
,也就是这行代码对 foo()
方法实现了增强。
这种方式属于编译时增强,和 Lombok 类似。
既然如此,那岂不是说使用这种方式时,没有 Spring 容器也能实现方法的增强?
确实如此。
1 2 3 4 5 6 7 8 9 public class A10Application { private static final Logger log = LoggerFactory.getLogger(A10Application.class); public static void main (String[] args) { MyService service = new MyService (); log.info("service class: {}" , service.getClass()); service.foo(); } }
indi.mofan.A10Application - service class: class indi.mofan.service.MyService
indi.mofan.aop.MyAspect - before()
indi.mofan.service.MyService - foo()
除此之外,使用这种方式,就算 foo()
方法是静态方法,也能够成功增强。
1 2 3 4 5 6 public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); public static void foo () { log.info("foo()" ); } }
1 2 3 4 5 public class A10Application { public static void main (String[] args) { MyService.foo(); } }
indi.mofan.aop.MyAspect - before()
indi.mofan.service.MyService - foo()
遇到的一些问题
答:本节只是做个小测试,实际开发时一般不会这么使用,因此移除 Lombok 的注解即可。比如本节采用的日志打印方式不再使用 @Slf4j
,而是使用了诸如以下的方式:
1 private static final Logger log = LoggerFactory.getLogger(MyService.class);
完全按照一样的步骤进行,但运行后的方法依旧没有增强。
答:这是由于 IDEA 在执行代码前又编译了一遍代码,覆盖了使用 Maven 编译生成字节码文件,导致增强失败。对 IDEA 进行设置,勾选 自动构建项目:
尽量使用 JDK8,因为 aspectj-maven-plugin
可能暂不支持高版本的 JDK。
10. Agent 类加载
重新创建一个 SpringBoot 项目,同样需要导入 AOP 相关的依赖。
一个 Service 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j @Service public class MyService { final public void foo () { log.info("foo()" ); bar(); } public void bar () { log.info("bar()" ); } }
一个切面类,注意这个切面类没有被 Spring 管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Slf4j @Aspect public class MyAspect { @Before("execution(* indi.mofan.service.MyService.*())") public void before () { log.info("before()" ); } }
一个用于测试的主启动类:
1 2 3 4 5 6 7 8 9 10 11 12 @Slf4j @SpringBootApplication public class A11Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A11Application.class, args); MyService service = context.getBean(MyService.class); log.info("service class: {}" , service.getClass()); service.foo(); } }
运行主启动类后,控制台会显示:
indi.mofan.A11Application : service class: class indi.mofan.service.MyService
indi.mofan.aop.MyAspect : before()
indi.mofan.service.MyService : foo()
indi.mofan.aop.MyAspect : before()
indi.mofan.service.MyService : bar()
如果完全按照上述步骤进行,会发现输出结果和给出的结果不一样。
那是怎么达到增强的效果呢?
首先得在 resources
目录下新建 META-INF
文件夹,并在 META-INF
目录下新建 aop.xml
文件,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 <aspectj > <aspects > <aspect name ="indi.mofan.aop.MyAspect" /> <weaver options ="-verbose -showWeaveInfo" > <include within ="indi.mofan.service.MyService" /> <include within ="indi.mofan.aop.MyAspect" /> </weaver > </aspects > </aspectj >
在运行 main()
方法前添加 VM options:
1 -javaagent:D:\environment\Maven\3.6.3-repository\.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar
其中的 D:\environment\Maven\3.6.3-repository\.m2
指本地 Maven 仓库地址,还需要确保本地仓库中存在 1.9.7 版本的 aspectjweaver,否则修改至对应版本。
这是控制台输出的信息就和前文的内容一样了。
从输出的内容可以看到 service.getClass()
打印出的信息也是原始类的 Class 信息,而非代理类的 Class 信息。因此不依赖 Spring 容器,直接 new
一个 MyService
实例并调用其 foo()
方法也能达到增强的目的。
如果查看 MyService
对应的 class 文件,会发现其内容并没有被修改,可以断定不是编译时增强,这里是在类加载时增强。
利用 Arthas 反编译类文件
可以借助阿里巴巴的 Arthas 来反编译加载的类文件,下载地址:下载 | arthas
在使用 Arthas 前,需要确保对应 Java 进程的存在,因此在上述代码中调用 service.foo();
方法后并没有关闭 Spring 容器。
解压下载的压缩包,进入 arthas-boot.jar
文件的同级目录,使用终端工具执行 java -jar .\arthas-boot.jar
:
运行之后会列举出存在的 Java 进程,找到需要连接的进程,之后输入目标进程对应的序号。当界面上成功显示 Arthas 的 Banner 时,证明连接成功:
输入 jad indi.mofan.service.MyService
表示需要反编译 MyService
:
可以看到 foo()
和 bar()
方法的第一行都被增加了一行代码,也就是这行代码对这两个方法实现了增强。
不仅如此,如果使用代理实现增强,被调用的 bar()
方法不会被成功增强,因为调用时默认使用了 this
关键词,表示调用的是原类中的方法,而不是代理类中的方法(经典面试题:@Transactional
注解失效的场景)。
11. 动态代理
11.1 JDK 动态代理
JDK 动态代理 只能 针对接口进行代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class JdkProxyDemo { interface Foo { void foo () ; } static final class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } } public static void main (String[] args) { Target target = new Target (); ClassLoader classLoader = JdkProxyDemo.class.getClassLoader(); Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class []{Foo.class}, (p, method, params) -> { System.out.println("before..." ); Object result = method.invoke(target, params); System.out.println("after..." ); return result; }); proxy.foo(); } }
运行 main()
方法后控制台打印出:
before...
target foo
after...
总结
代理对象和目标对象是兄弟关系,都实现了相同的接口,因此不能将代理对象强转成目标对象类型;
代理类与目标类之间没有继承关系,因此目标类可以被 final
修饰。
11.2 CGLib 动态代理
CGLib 动态代理与 JDK 动态代理不一样,无需目标类实现某个特定的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class CglibProxyDemo { static class Target { public void foo () { System.out.println("target foo" ); } } public static void main (String[] args) { Target target = new Target (); Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, params, methodProxy) -> { System.out.println("before..." ); Object result = method.invoke(target, params); System.out.println("after..." ); return result; }); proxy.foo(); } }
运行 main()
方法后控制台打印出:
before...
target foo
after...
调用目标方法的方式有三种,上文使用的是:
1 Object result = method.invoke(target, params);
使用这种方式,将利用反射对目标方法进行调用。
还可以使用 methodProxy
不利用反射对目标方法进行调用:
1 2 3 4 Object result = methodProxy.invoke(target, args); Object result = methodProxy.invokeSuper(obj, args);
总结
与 JDK 动态代理相比,CGLib 动态代理无需实现接口
代理对象和目标对象是父子关系,也就是说代理类继承了目标类
由于代理类继承了目标类,因此目标类不能被 final
修饰,否则将出现以下异常信息:
java.lang.IllegalArgumentException: Cannot subclass final class indi.mofan.a12.CglibProxyDemo$Target
代理类继承目标类后,还会重写目标类中要求被增强的方法,因此被增强的方法不能被 final
修饰,否则将无法被增强,但不会抛出异常
12. JDK 动态代理原理
12.1 JDK 动态代理的模拟
先来模拟一下 JDK 动态代理的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 interface Foo { void foo () ; } static class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } } public class $Proxy0 implements A13 .Foo{ @Override public void foo () { System.out.println("before..." ); new A13 .Target().foo(); } } public static void main (String[] args) { $Proxy0 proxy = new $Proxy0 (); proxy.foo(); }
运行 main()
方法,控制台打印出:
before...
target foo
代码的实现很简单,但仔细想一下,如果是 JDK 中的实现:
“功能增强”的代码实现会直接硬编码吗?直接打印?
“调用目标”的代码就这样直接写上去?存不存在满足某些条件才调用目标的场景呢?
也就是说,“功能增强”和“调用目标”这两部分的代码都是不确定的。
针对这种“不确定”的实现,可以提供一个抽象类,等到用户具体使用时才实现抽象类,重写抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 interface Foo { void foo () ; } static class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } } interface InvocationHandler { void invoke () ; } public class $Proxy0 implements A13 .Foo{ private final A13.InvocationHandler h; public $Proxy0(A13.InvocationHandler h) { this .h = h; } @Override public void foo () { h.invoke(); } } public static void main (String[] args) { $Proxy0 proxy = new $Proxy0 (new InvocationHandler () { @Override public void invoke () { System.out.println("before..." ); new A13 .Target().foo(); } }); proxy.foo(); }
运行 main()
方法,控制台依旧成功打印出:
before...
target foo
多个抽象方法的接口
这样的实现依旧有问题,如果接口中提供了两个抽象方法呢?比如:
1 2 3 4 5 interface Foo { void foo () ; void bar () ; }
此时无论是目标类,还是代理类都要重写这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 static class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } @Override public void bar () { System.out.println("target bar" ); } } public class $Proxy0 implements A13 .Foo{ private final A13.InvocationHandler h; public $Proxy0(A13.InvocationHandler h) { this .h = h; } @Override public void foo () { h.invoke(); } @Override public void bar () { h.invoke(); } } public static void main (String[] args) { $Proxy0 proxy = new $Proxy0 (new InvocationHandler () { @Override public void invoke () { System.out.println("before..." ); new A13 .Target().foo(); } }); proxy.foo(); proxy.bar(); }
此时再执行 main()
方法,控制台上打印出:
before...
target foo
before...
target foo
打印结果有点问题。当调用代理对象的 bar()
方法时,输出了 target foo
,而不是 bar()
方法应该打印的 target bar
。
原因就出在实现 InvocationHandler
的 invoke()
方法时,依旧只调用了目标类的 foo()
方法,而不是 bar()
方法。
也就是说,在调用代理对象中的某个方法时,增强的应该是目标对象中对应的方法,希望在调用目标方法时能够动态编码。
那么可以在 invoke()
方法中添加两个入参,分别表示需要调用的目标方法和目标方法的参数:
1 2 3 interface InvocationHandler { void invoke (Method method, Object[] params) throws Throwable; }
增加参数之后需要修改代理类,并将实现的抽象方法的 Method
对象与参数传递给 invoke()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class $Proxy0 implements A13 .Foo{ private final A13.InvocationHandler h; public $Proxy0(A13.InvocationHandler h) { this .h = h; } @Override @SneakyThrows public void foo () { Method method = A13.Foo.class.getMethod("foo" ); h.invoke(method, new Object [0 ]); } @Override @SneakyThrows public void bar () { Method method = A13.Foo.class.getMethod("bar" ); h.invoke(method, new Object [0 ]); } }
还需要修改下 main()
方法中 InvocationHandler
的实现,利用传递的 Method
对象和参数信息反射调用目标方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) { $Proxy0 proxy = new $Proxy0 (new InvocationHandler () { @Override public void invoke (Method method, Object[] params) throws Throwable { System.out.println("before..." ); Object invoke = method.invoke(new Target (), params); } }); proxy.foo(); proxy.bar(); }
再执行 main()
方法,控制台上打印出:
before...
target foo
before...
target bar
有返回值的抽象方法
优化还在继续,如果抽象方法有返回值呢?比如:
1 2 3 4 5 interface Foo { void foo () ; int bar () ; }
实现了这个接口的目标类和代理类重写的方法都需要有具体的返回值:
1 2 3 4 5 6 7 8 9 10 11 12 static class Target implements Foo { @Override public void foo () { System.out.println("target foo" ); } @Override public int bar () { System.out.println("target bar" ); return 100 ; } }
目标类很简单,直接返回,那代理类返回什么?
InvocationHandler
的 invoke()
方法是对“功能增强”和“调用目标”的抽象,因此可以使 invoke()
方法也返回一个值,返回的值即为目标方法的返回值,这样就可以使得代理类中的方法有值可返。
1 2 3 interface InvocationHandler { Object invoke (Method method, Object[] params) throws Throwable; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class $Proxy0 implements A13 .Foo { private final A13.InvocationHandler h; public $Proxy0(A13.InvocationHandler h) { this .h = h; } @Override public void foo () { try { Method foo = A13.Foo.class.getMethod("foo" ); h.invoke(foo, new Object [0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public int bar () { try { Method bar = A13.Foo.class.getMethod("bar" ); return (int ) h.invoke(bar, new Object [0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } }
修改 main()
方法,打印 bar()
方法的返回值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) { $Proxy0 proxy = new $Proxy0 (new InvocationHandler () { @Override public Object invoke (Method method, Object[] params) throws Throwable { System.out.println("before..." ); return method.invoke(new Target (), params); } }); proxy.foo(); System.out.println(proxy.bar()); }
运行结果如下:
before...
target foo
before...
target bar
100
在静态代码块里创建 Method
实例
每调用一次代理对象中的方法都会创建一个 Method
实例,这些实例是可以复用的,因此可以将这些实例的创建移动到静态代码块中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class $Proxy0 implements A13 .Foo { private final A13.InvocationHandler h; public $Proxy0(A13.InvocationHandler h) { this .h = h; } @Override public void foo () { try { h.invoke(foo, new Object [0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public int bar () { try { return (int ) h.invoke(bar, new Object [0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } static Method foo; static Method bar; static { try { foo = A13.Foo.class.getMethod("foo" ); bar = A13.Foo.class.getMethod("bar" ); } catch (NoSuchMethodException e) { throw new NoSuchMethodError (e.getMessage()); } } }
invoke()
方法增加代理对象作为参数
在 JDK 提供的 InvocationHandler
接口的 invoke()
方法还将代理对象作为方法的参数,以便用户根据实际情况使用。继续修改自定义的 InvocationHandler
接口:
1 2 3 interface InvocationHandler { Object invoke (Object proxy, Method method, Object[] params) throws Throwable; }
修改代理类中对 invoke()
方法的调用,第一个参数为当前类的实例,即 this
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class $Proxy0 implements A13 .Foo { @Override public void foo () { try { h.invoke(this , foo, new Object [0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public int bar () { try { return (int ) h.invoke(this , bar, new Object [0 ]); } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } }
main()
方法重写的 invoke()
方法也要增加 proxy
参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) { $Proxy0 proxy = new $Proxy0 (new InvocationHandler () { @Override public Object invoke (Object proxy, Method method, Object[] params) throws Throwable { System.out.println("before..." ); return method.invoke(new Target (), params); } }); proxy.foo(); System.out.println(proxy.bar()); }
运行 main()
方法,结果不发生变化。
向 JDK 靠齐
到此为止,自定义的 InvocationHandler
接口与 JDK 提供的 InvocationHandler
接口无异,注释自定义的 InvocationHandler
,更换为 JDK 提供的 InvocationHandler
接口。
在 JDK 提供的 InvocationHandler
接口的注释中有一句:@see Proxy
,在 Proxy
类的代码中有:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Proxy implements java .io.Serializable { protected InvocationHandler h; protected Proxy (InvocationHandler h) { Objects.requireNonNull(h); this .h = h; } }
Proxy
类中有一个 InvocationHandler
对象的成员变量。
因此还可以使代理类 $Proxy0
继承 Proxy
来进一步减少代码:
1 2 3 4 5 6 7 8 9 10 11 12 import java.lang.reflect.InvocationHandler;public class $Proxy0 extends Proxy implements A13 .Foo { private static final long serialVersionUID = -6909541593982979501L ; public $Proxy0(InvocationHandler h) { super (h); } }
12.2 代理类的源码
JDK 动态代理生成的代理类是以字节码的形式存在的,并不存在所谓的 .java
文件,但也不是说就没办法看到生成的代理类信息了。
以【11.1 JDK 动态代理】中的程序为例,查看 JDK 生成的代理类信息。
利用 Arthas 反编译代理类字节码文件
如果要使用 Arthas 的反编译功能需要满足两个条件:
知道被反编译文件的全限定类名
程序不能中断,需要存在 Java 进程
为了满足这个条件,可以在控制台打印出生成的代理类的全限定类名,然后利用阻塞 IO 使程序不中断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @SneakyThrows public static void main (String[] args) { Target target = new Target (); ClassLoader classLoader = JdkProxyDemo.class.getClassLoader(); Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class []{Foo.class}, (p, method, params) -> { System.out.println("before..." ); Object result = method.invoke(target, params); System.out.println("after..." ); return result; }); System.out.println(proxy.getClass()); proxy.foo(); System.in.read(); }
运行 main()
方法后,控制台打印出:
class indi.mofan.a12.$Proxy0
before...
target foo
after...
其中的 indi.mofan.a12.$Proxy0
就是生成的代理类的全限定类名,可以把它复制下来,之后按照【10. Agent 类加载】中使用 Arthas 的方式 indi.mofan.a12.$Proxy0
进行反编译即可。
将生成的代理类字节码文件保存在磁盘上
除了借助外部工具外,还可以直接将 JDK 生成的代理类字节码文件保存在磁盘上,其做法与【Lambda 与序列化】一文中将函数式接口动态生成的 Class 保存到磁盘上类似。
可以在 main()
方法开头加一句:
1 2 3 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" ); System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles" , "true" );
不同版本的 JDK 添加的配置信息不同,至于具体是哪一个可以查看 JDK 中 ProxyGenerator
类中的 saveGeneratedFiles
成员变量,比如:
1 private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction ("sun.misc.ProxyGenerator.saveGeneratedFiles" ));
显然,此处应该使用第一种方式来设置系统属性。最终的 main()
方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void main (String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" ); Target target = new Target (); ClassLoader classLoader = JdkProxyDemo.class.getClassLoader(); Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class []{Foo.class}, (p, method, params) -> { }); proxy.foo(); }
除此之外,还可以通过在运行前添加 VM options:
1 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
如果既在代码里设置了系统属性,又配置了 VM options,最终以代码中的配置为主。
运行 main()
方法,在当前项目目录下生成 indi.mofan.a12.$Proxy0.class
文件,查看其内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 final class $Proxy0 extends Proxy implements Foo { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super (var1); } public final boolean equals (Object var1) throws { try { return (Boolean)super .h.invoke(this , m1, new Object []{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException (var4); } } public final String toString () throws { try { return (String)super .h.invoke(this , m2, (Object[])null ); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException (var3); } } public final void foo () throws { try { super .h.invoke(this , m3, (Object[])null ); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException (var3); } } public final int hashCode () throws { try { return (Integer)super .h.invoke(this , m0, (Object[])null ); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException (var3); } } static { try { m1 = Class.forName("java.lang.Object" ).getMethod("equals" , Class.forName("java.lang.Object" )); m2 = Class.forName("java.lang.Object" ).getMethod("toString" ); m3 = Class.forName("indi.mofan.a12.JdkProxyDemo$Foo" ).getMethod("foo" ); m0 = Class.forName("java.lang.Object" ).getMethod("hashCode" ); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError (var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError (var3.getMessage()); } } }
其内容与自定义的 $Proxy0
几乎无异,只不过 JDK 生成的代理类信息还生成 equals()
、toString()
和 hashCode()
三个方法对应的 Method
对象,并对它们也进行了相同的增强。
12.3 JDK 代理类字节码生成
JDK 在生成代理类时,没有经历源码阶段、编译阶段,而是直接到字节码阶段,使用了 ASM 来完成。
ASM 的学习成本较高,在此不做过多介绍,本节将采用一直“曲线求国”的方式,使用 IDEA 的 ASM Bytecode outline
插件将 Java 源码转换成使用 ASM 编写的代码。
ASM Bytecode outline
插件在高版本 Java 中可能无法很好地工作,建议在 Java8 环境下使用。
自行编写一个接口和代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public interface Foo { void foo () ; } public class $Proxy0 extends Proxy implements Foo { private static final long serialVersionUID = 6059465134835974286L ; static Method foo; static { try { foo = Foo.class.getMethod("foo" ); } catch (NoSuchMethodException e) { throw new NoSuchMethodError (e.getMessage()); } } public $Proxy0(InvocationHandler h) { super (h); } @Override public void foo () { try { this .h.invoke(this , foo, null ); } catch (Throwable throwable) { throw new UndeclaredThrowableException (throwable); } } }
将上述代码进行编译,编译成功后在 $Proxy0
文件中右击,选择 Show Bytecode outline 浏览当前类对应的字节码信息:
查看 ASMified,并拷贝其内容,复制到 $Proxy0Dump
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 import jdk.internal.org.objectweb.asm.*;public class $Proxy0Dump implements Opcodes { public static byte [] dump() throws Exception { ClassWriter cw = new ClassWriter (0 ); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cw.visit(52 , ACC_PUBLIC + ACC_SUPER, "indi/mofan/$Proxy0" , null , "java/lang/reflect/Proxy" , new String []{"indi/mofan/Foo" }); cw.visitSource("$Proxy0.java" , null ); { fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "serialVersionUID" , "J" , null , new Long (6059465134835974286L )); fv.visitEnd(); } { fv = cw.visitField(ACC_STATIC, "foo" , "Ljava/lang/reflect/Method;" , null , null ); fv.visitEnd(); } { mv = cw.visitMethod(ACC_PROTECTED, "<init>" , "(Ljava/lang/reflect/InvocationHandler;)V" , null , null ); mv.visitParameter("h" , 0 ); mv.visitCode(); Label l0 = new Label (); mv.visitLabel(l0); mv.visitLineNumber(26 , l0); mv.visitVarInsn(ALOAD, 0 ); mv.visitVarInsn(ALOAD, 1 ); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/Proxy" , "<init>" , "(Ljava/lang/reflect/InvocationHandler;)V" , false ); Label l1 = new Label (); mv.visitLabel(l1); mv.visitLineNumber(27 , l1); mv.visitInsn(RETURN); Label l2 = new Label (); mv.visitLabel(l2); mv.visitLocalVariable("this" , "Lindi/mofan/$Proxy0;" , null , l0, l2, 0 ); mv.visitLocalVariable("h" , "Ljava/lang/reflect/InvocationHandler;" , null , l0, l2, 1 ); mv.visitMaxs(2 , 2 ); mv.visitEnd(); } { mv = cw.visitMethod(ACC_PUBLIC, "foo" , "()V" , null , null ); mv.visitCode(); Label l0 = new Label (); Label l1 = new Label (); Label l2 = new Label (); mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Throwable" ); mv.visitLabel(l0); mv.visitLineNumber(32 , l0); mv.visitVarInsn(ALOAD, 0 ); mv.visitFieldInsn(GETFIELD, "indi/mofan/$Proxy0" , "h" , "Ljava/lang/reflect/InvocationHandler;" ); mv.visitVarInsn(ALOAD, 0 ); mv.visitFieldInsn(GETSTATIC, "indi/mofan/$Proxy0" , "foo" , "Ljava/lang/reflect/Method;" ); mv.visitInsn(ACONST_NULL); mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler" , "invoke" , "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;" , true ); mv.visitInsn(POP); mv.visitLabel(l1); mv.visitLineNumber(35 , l1); Label l3 = new Label (); mv.visitJumpInsn(GOTO, l3); mv.visitLabel(l2); mv.visitLineNumber(33 , l2); mv.visitFrame(Opcodes.F_SAME1, 0 , null , 1 , new Object []{"java/lang/Throwable" }); mv.visitVarInsn(ASTORE, 1 ); Label l4 = new Label (); mv.visitLabel(l4); mv.visitLineNumber(34 , l4); mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException" ); mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 1 ); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException" , "<init>" , "(Ljava/lang/Throwable;)V" , false ); mv.visitInsn(ATHROW); mv.visitLabel(l3); mv.visitLineNumber(36 , l3); mv.visitFrame(Opcodes.F_SAME, 0 , null , 0 , null ); mv.visitInsn(RETURN); Label l5 = new Label (); mv.visitLabel(l5); mv.visitLocalVariable("throwable" , "Ljava/lang/Throwable;" , null , l4, l3, 1 ); mv.visitLocalVariable("this" , "Lindi/mofan/$Proxy0;" , null , l0, l5, 0 ); mv.visitMaxs(4 , 2 ); mv.visitEnd(); } { mv = cw.visitMethod(ACC_STATIC, "<clinit>" , "()V" , null , null ); mv.visitCode(); Label l0 = new Label (); Label l1 = new Label (); Label l2 = new Label (); mv.visitTryCatchBlock(l0, l1, l2, "java/lang/NoSuchMethodException" ); mv.visitLabel(l0); mv.visitLineNumber(19 , l0); mv.visitLdcInsn(Type.getType("Lindi/mofan/Foo;" )); mv.visitLdcInsn("foo" ); mv.visitInsn(ICONST_0); mv.visitTypeInsn(ANEWARRAY, "java/lang/Class" ); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class" , "getMethod" , "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;" , false ); mv.visitFieldInsn(PUTSTATIC, "indi/mofan/$Proxy0" , "foo" , "Ljava/lang/reflect/Method;" ); mv.visitLabel(l1); mv.visitLineNumber(22 , l1); Label l3 = new Label (); mv.visitJumpInsn(GOTO, l3); mv.visitLabel(l2); mv.visitLineNumber(20 , l2); mv.visitFrame(Opcodes.F_SAME1, 0 , null , 1 , new Object []{"java/lang/NoSuchMethodException" }); mv.visitVarInsn(ASTORE, 0 ); Label l4 = new Label (); mv.visitLabel(l4); mv.visitLineNumber(21 , l4); mv.visitTypeInsn(NEW, "java/lang/NoSuchMethodError" ); mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 0 ); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/NoSuchMethodException" , "getMessage" , "()Ljava/lang/String;" , false ); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoSuchMethodError" , "<init>" , "(Ljava/lang/String;)V" , false ); mv.visitInsn(ATHROW); mv.visitLabel(l3); mv.visitLineNumber(23 , l3); mv.visitFrame(Opcodes.F_SAME, 0 , null , 0 , null ); mv.visitInsn(RETURN); mv.visitLocalVariable("e" , "Ljava/lang/NoSuchMethodException;" , null , l4, l3, 0 ); mv.visitMaxs(3 , 1 ); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } }
编写测试方法使用 $Proxy0Dump
生成 $Proxy0
的 class 文件:
1 2 3 4 5 6 7 8 9 public class TestProxy { public static void main (String[] args) throws Exception { byte [] dump = $Proxy0Dump.dump(); FileOutputStream os = new FileOutputStream ("$Proxy0.class" ); os.write(dump, 0 , dump.length); os.close(); } }
运行 main()
方法后,在工作目录下生成 $Proxy0.class
。
工作目录查看方式:
也就是说会在 D:\Code\IdeaCode\advanced-spring
目录下生成 $Proxy0.class
文件,IDEA 反编译后的内容与手动编写的 $Proxy0.java
文件的内容无异。
实际使用时并不需要使用 $Proxy0Dump
生成 $Proxy.class
文件,而是利用 ClassLoader
直接加载类信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void main (String[] args) throws Exception { byte [] dump = $Proxy0Dump.dump(); ClassLoader classLoader = new ClassLoader () { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { return super .defineClass(name, dump, 0 , dump.length); } }; Class<?> proxyClass = classLoader.loadClass("indi.mofan.$Proxy0" ); Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class); Foo fooProxy = (Foo) constructor.newInstance((InvocationHandler) (proxy, method, args1) -> { System.out.println("before..." ); System.out.println("模拟调用目标" ); return null ; }); fooProxy.foo(); }
运行上述代码后,控制台打印出:
before...
模拟调用目标
12.4 JDK 反射优化
使用 JDK 的动态代理时,会使用反射调用方法:
1 Object result = method.invoke(target, params);
相比于正常调用方法,利用反射的性能要稍微低一些,JDK 有怎么反射进行优化吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class TestMethodProxy { public static void main (String[] args) throws Exception { Method foo = TestMethodProxy.class.getMethod("foo" , int .class); for (int i = 1 ; i <= 17 ; i++) { show(i, foo); foo.invoke(null , i); } System.in.read(); } private static void show (int i, Method foo) throws Exception { Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor" ); getMethodAccessor.setAccessible(true ); Object invoke = getMethodAccessor.invoke(foo); if (invoke == null ) { System.out.println(i + ":" + null ); return ; } Field delegate = Class.forName("sun.reflect.DelegatingMethodAccessorImpl" ).getDeclaredField("delegate" ); delegate.setAccessible(true ); System.out.println(i + ": " + delegate.get(invoke)); } public static void foo (int i) { System.out.println(i + ": foo" ); } }
运行 main()
方法后,控制台打印出:
1: null
1: foo
2: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
2: foo
3: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
3: foo
4: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
4: foo
5: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
5: foo
6: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
6: foo
7: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
7: foo
8: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
8: foo
9: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
9: foo
10: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
10: foo
11: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
11: foo
12: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
12: foo
13: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
13: foo
14: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
14: foo
15: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
15: foo
16: sun.reflect.NativeMethodAccessorImpl@1be6f5c3
16: foo
17: sun.reflect.GeneratedMethodAccessor2@5b2133b1
17: foo
从上述信息可知,第一次调用时没有使用 MethodAccessor
对象,从第二次到第十六次,使用了 NativeMethodAccessorImpl
对象,而在第十七次使用了 GeneratedMethodAccessor2
对象。
NativeMethodAccessorImpl
基于 Java 本地 API 实现,性能较低,第十七次调用换成 GeneratedMethodAccessor2
后,性能得到一定的提升。
使用 Arthas 反编译查看 GeneratedMethodAccessor2
类中的信息,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class GeneratedMethodAccessor2 extends MethodAccessorImpl { public Object invoke (Object object, Object[] objectArray) throws InvocationTargetException { try { TestMethodProxy.foo((int )c); return null ; } catch (Throwable throwable) { throw new InvocationTargetException (throwable); } catch (ClassCastException | NullPointerException runtimeException) { throw new IllegalArgumentException (super .toString()); } } }
在反编译得到的代码中,不再是通过反射调用方法,而是直接正常调用方法,即:
1 TestMethodProxy.foo((int )c);
因此性能得到了提升,但这样的提升也是有一定代价的:为优化 一个 方法的反射调用,生成了一个 GeneratedMethodAccessor2
代理类。
13. CGLib 动态代理原理
13.1 CGLib 动态代理的模拟
同样先模拟下 CGLib 动态代理的模拟:
被代理的类,即目标类:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Target { public void save () { System.out.println("save()" ); } public void save (int i) { System.out.println("save(int)" ); } public void save (long i) { System.out.println("save(long)" ); } }
CGLib 动态代理生成的代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class Proxy extends Target { private MethodInterceptor methodInterceptor; public void setMethodInterceptor (MethodInterceptor methodInterceptor) { this .methodInterceptor = methodInterceptor; } static Method save0; static Method save1; static Method save2; static { try { save0 = Target.class.getMethod("save" ); save1 = Target.class.getMethod("save" , int .class); save2 = Target.class.getMethod("save" , long .class); } catch (NoSuchMethodException e) { throw new NoSuchMethodError (e.getMessage()); } } @Override public void save () { try { methodInterceptor.intercept(this , save0, new Object [0 ], null ); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public void save (int i) { try { methodInterceptor.intercept(this , save1, new Object []{i}, null ); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public void save (long i) { try { methodInterceptor.intercept(this , save2, new Object []{i}, null ); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) { Target target = new Target (); Proxy proxy = new Proxy (); proxy.setMethodInterceptor(new MethodInterceptor () { @Override public Object intercept (Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before" ); return method.invoke(target, args); } }); proxy.save(); proxy.save(1 ); proxy.save(2L ); }
运行 main()
方法后,控制台打印出:
before
save()
before
save(int)
before
save(long)
13.2 MethodProxy
在上述 Proxy
类中,重写了父类中的方法,并在重写的方法中调用了 intercept()
方法,重写的这些方法相当于是带增强功能的方法。
在 JDK 的动态代理中,使用反射对方法进行调用,而在 CGLib 动态代理中,可以使用 intercept()
方法中 MethodProxy
类型的参数实现不经过反射来调用方法。
接收的 MethodProxy
类型的参数可以像 Method
类型的参数一样,在静态代码块中被实例化。
可以通过静态方法 MethodProxy.create()
来创建 MethodProxy
对象:
1 2 3 4 5 6 7 public static MethodProxy create (Class c1, Class c2, String desc, String name1, String name2) { MethodProxy proxy = new MethodProxy (); proxy.sig1 = new Signature (name1, desc); proxy.sig2 = new Signature (name2, desc); proxy.createInfo = new CreateInfo (c1, c2); return proxy; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public class Proxy extends Target { private MethodInterceptor methodInterceptor; public void setMethodInterceptor (MethodInterceptor methodInterceptor) { this .methodInterceptor = methodInterceptor; } static Method save0; static Method save1; static Method save2; static MethodProxy save0Proxy; static MethodProxy save1Proxy; static MethodProxy save2Proxy; static { try { save0 = Target.class.getMethod("save" ); save1 = Target.class.getMethod("save" , int .class); save2 = Target.class.getMethod("save" , long .class); save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V" , "save" , "saveSuper" ); save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V" , "save" , "saveSuper" ); save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V" , "save" , "saveSuper" ); } catch (NoSuchMethodException e) { throw new NoSuchMethodError (e.getMessage()); } } public void saveSuper () { super .save(); } public void saveSuper (int i) { super .save(i); } public void saveSuper (long i) { super .save(i); } @Override public void save () { try { methodInterceptor.intercept(this , save0, new Object [0 ], save0Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public void save (int i) { try { methodInterceptor.intercept(this , save1, new Object []{i}, save1Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public void save (long i) { try { methodInterceptor.intercept(this , save2, new Object []{i}, save2Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } }
在 main()
方法中不再使用 Method
类型的参数对方法进行调用,而是使用 MethodProxy
类型的参数:
1 2 return methodProxy.invoke(target, args);
或者:
1 2 return methodProxy.invokeSuper(o, args);
14. MethodProxy 原理
调用 methodProxy.invoke()
方法时,会额外使用一个代理类,该代理类配合目标对象使用。调用 methodProxy.invokeSuper()
方法时,也会额外使用一个代理类,该代理类配合代理对象使用。
当调用 MethodProxy
对象的 invoke()
方法或 invokeSuper()
方法时,就会生成这两个代理类,它们都继承至 FastClass
。
FastClass
是一个抽象类,其内部有多个抽象方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 public abstract class FastClass { public abstract int getIndex (String var1, Class[] var2) ; public abstract int getIndex (Class[] var1) ; public abstract Object invoke (int var1, Object var2, Object[] var3) throws InvocationTargetException; public abstract Object newInstance (int var1, Object[] var2) throws InvocationTargetException; public abstract int getIndex (Signature signature) ; public abstract int getMaxIndex () ; }
重点讲解 invoke()
方法与 getIndex(Signature signature)
方法。
模拟生成的与目标类相关的代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 public class TargetFastClass { static Signature s0 = new Signature ("save" , "()V" ); static Signature s1 = new Signature ("save" , "(I)V" ); static Signature s2 = new Signature ("save" , "(J)V" ); public int getIndex (Signature signature) { if (s0.equals(signature)) { return 0 ; } if (s1.equals(signature)) { return 1 ; } if (s2.equals(signature)) { return 2 ; } return -1 ; } public Object invoke (int index, Object target, Object[] args) { if (index == 0 ) { ((Target) target).save(); return null ; } if (index == 1 ) { ((Target) target).save((int ) args[0 ]); return null ; } if (index == 2 ) { ((Target) target).save((long ) args[0 ]); return null ; } throw new RuntimeException ("无此方法" ); } public static void main (String[] args) { TargetFastClass fastClass = new TargetFastClass (); int index = fastClass.getIndex(new Signature ("save" , "()V" )); fastClass.invoke(index, new Target (), new Object [0 ]); index = fastClass.getIndex(new Signature ("save" , "(J)V" )); fastClass.invoke(index, new Target (), new Object []{2L }); } }
运行 main()
方法后,控制台打印出:
save()
save(long)
模拟生成的与代理类相关的代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public class ProxyFastClass { static Signature s0 = new Signature ("saveSuper" , "()V" ); static Signature s1 = new Signature ("saveSuper" , "(I)V" ); static Signature s2 = new Signature ("saveSuper" , "(J)V" ); public int getIndex (Signature signature) { if (s0.equals(signature)) { return 0 ; } if (s1.equals(signature)) { return 1 ; } if (s2.equals(signature)) { return 2 ; } return -1 ; } public Object invoke (int index, Object proxy, Object[] args) { if (index == 0 ) { ((Proxy) proxy).saveSuper(); return null ; } if (index == 1 ) { ((Proxy) proxy).saveSuper((int ) args[0 ]); return null ; } if (index == 2 ) { ((Proxy) proxy).saveSuper((long ) args[0 ]); return null ; } throw new RuntimeException ("无此方法" ); } public static void main (String[] args) { ProxyFastClass fastClass = new ProxyFastClass (); int index = fastClass.getIndex(new Signature ("saveSuper" , "()V" )); fastClass.invoke(index, new Proxy (), new Object [0 ]); } }
运行 main()
方法后,控制台打印出:
save()
总结
调用 MethodProxy.create()
方法创建 MethodProxy
对象时,要求传递带增强功能的方法名称、带原始功能的方法名称以及方法描述符。
根据两个方法名称和方法描述符可以在调用生成的两个代理类中的 getIndex()
方法时获取被增强方法的编号,之后:
调用 methodProxy.invoke()
方法时,就相当于调用 TargetFastClass
中的 invoke()
方法,并在这个 invoke()
方法中正常调用目标对象方法(Spring 底层的选择)。
调用 methodProxy.invokeSuper()
方法时,就相当于调用 ProxyFastClass
中的 invoke()
方法,并在这个 invoke()
方法中正常调用代理对象中带原始功能的方法。
与 JDK 中优化反射调用方法的对比
在 JDK 中需要反射调用 16 次方法后才会生成优化反射调用的代理类,而在 CGLib 中,当调用 MethodProxy.create()
方法时就会生成由于优化反射调用的代理类;
在 JDK 中一个方法的反射调用优化就要生成一个代理类,而在 CGLib 中,一个代理类生成两个 FastClass
代理类。
15. JDK 和 CGLib 的统一
15.1 advisor
切面有 aspect
和 advisor
两个概念,aspect
是多组通知(advice)和切点(pointcut)的组合,也是实际编码时使用的,advisor
则是更细粒度的切面,仅包含一个通知和切点,aspect
在生效之前会被拆解成多个 advisor
。
Spring 中对切点、通知、切面的抽象如下:
classDiagram
class Advisor
class PointcutAdvisor
class Pointcut
class Advice
class MethodInterceptor
class AspectJExpressionPointcut
Pointcut <|-- AspectJExpressionPointcut
Advice <|-- MethodInterceptor
Advisor <|-- PointcutAdvisor
PointcutAdvisor o-- Pointcut
PointcutAdvisor o-- Advice
<<interface>> Advisor
<<interface>> PointcutAdvisor
<<interface>> Advice
<<interface>> Pointcut
<<interface>> MethodInterceptor
切点:即 Pointcut
,其典型实现是 AspectJExpressionPointcut
通知:即 Advice
,其典型子类接口为 MethodInterceptor
,表示环绕通知
切面:即 Advisor
,仅包含一个切点和通知
本节将重点介绍 advisor
切面。
15.2 切面与代理对象的创建
通过以下四步创建切面和代理:
备好切点
备好通知
备好切面
创建代理
在 Spring 中,切点通过接口 org.springframework.aop.Pointcut
来表示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface Pointcut { ClassFilter getClassFilter () ; MethodMatcher getMethodMatcher () ; Pointcut TRUE = TruePointcut.INSTANCE; }
Pointcut
接口有很多实现类,比如:
AnnotationMatchingPointcut
:通过注解进行匹配
AspectJExpressionPointcut
:通过 AspectJ 表达式进行匹配(本节的选择)
在 Spring 中,通知的表示也有很多接口,在此介绍最基本、最重要的接口 org.aopalliance.intercept.MethodInterceptor
,这个接口实现的通知属于环绕通知。
在 Spring 中,切面的实现也有很多,在此选择 DefaultPointcutAdvisor
,创建这种切面时,传递一个节点和通知。
最后创建代理对象时,无需显式实现 JDK 动态代理或 CGLib 动态代理,Spring 提供了名为 ProxyFactory
的工厂,其内部通过不同的情况选择不同的代理实现,更方便地创建代理对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 interface I1 { void foo () ; void bar () ; } static class Target1 implements I1 { @Override public void foo () { System.out.println("target1 foo" ); } @Override public void bar () { System.out.println("target1 bar" ); } } static class Target2 { public void foo () { System.out.println("target2 foo" ); } public void bar () { System.out.println("target2 bar" ); } } public static void main (String[] args) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression("execution(* foo())" ); MethodInterceptor advice = invocation -> { System.out.println("before..." ); Object result = invocation.proceed(); System.out.println("after..." ); return result; }; DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor (pointcut, advice); Target1 target = new Target1 (); ProxyFactory factory = new ProxyFactory (); factory.setTarget(target); factory.addAdvisor(advisor); I1 proxy = (I1) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar(); }
运行 main()
方法,控制台打印出:
class indi.mofan.a15.A15$Target1$$EnhancerBySpringCGLIB$$381723d1
before...
target1 foo
after...
target1 bar
foo()
方法被增强,但 bar()
并没有,并且选择了 CGLib 动态代理作为代理的实现。
Spring 是根据什么信息来选择不同的动态代理实现呢?
ProxyFactory
的父类 ProxyConfig
中有个名为 proxyTargetClass
的布尔类型成员变量:
当 proxyTargetClass == false
,并且目标对象所在类实现了接口时,将选择 JDK 动态代理;
当 proxyTargetClass == false
,但目标对象所在类未实现接口时,将选择 CGLib 动态代理;
当 proxyTargetClass == true
,总是选择 CGLib 动态代理。
上文中的 target
对象的所在类 Targer1
实现了 I1
接口,最终为什么依旧选择了 CGLib 动态代理作为代理类的创建方式呢?
这是因为并没有显式这是 target
对象的实现类,Spring 认为其并未实现接口。
设置 factory
对象的 interfaces
信息:
1 factory.setInterfaces(target.getClass().getInterfaces());
之后再运行 main()
,控制台打印出:
class indi.mofan.a15.$Proxy0
before...
target1 foo
after...
target1 bar
此时选择的动态代理实现方式是 JDK 动态代理。
再设置 factory
对象的 proxyTargetClass
为 true
:
1 factory.setProxyTargetClass(true );
运行 main()
方法后,控制台打印出以下内容,选择 CGLib 动态代理作为动态代理的实现方式:
class indi.mofan.a15.A15$Target1$$EnhancerBySpringCGLIB$$34c2d9b8
before...
target1 foo
after...
target1 bar
再将 proxyTargetClass
的值修改回 false
,并修改目标对象的所在类为 Target2
,Target2
并未实现任何接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void main (String[] args) { Target2 target = new Target2 (); ProxyFactory factory = new ProxyFactory (); factory.setTarget(target); factory.addAdvisor(advisor); factory.setInterfaces(target.getClass().getInterfaces()); factory.setProxyTargetClass(false ); Target2 proxy = (Target2) factory.getProxy(); System.out.println(proxy.getClass()); proxy.foo(); proxy.bar(); }
运行 main()
方法后,控制台打印出以下内容,依旧选择 CGLib 动态代理作为动态代理的实现方式:
class indi.mofan.a15.A15$Target2$$EnhancerBySpringCGLIB$$4bb2ac74
before...
target2 foo
after...
target2 bar
ProxyFactory
是用来创建代理的核心实现,使用 AopProxyFactory
选择具体的代理实现:
JdkDynamicAopProxy
ObjenesisCglibAopProxy
classDiagram
Advised <|-- ProxyFactory
ProxyFactory o-- Target
ProxyFactory o-- "多" Advisor
ProxyFactory --> AopProxyFactory : 使用
AopProxyFactory --> AopProxy
Advised <|-- 基于CGLIB的Proxy
基于CGLIB的Proxy <-- ObjenesisCglibAopProxy : 创建
AopProxy <|-- ObjenesisCglibAopProxy
AopProxy <|-- JdkDynamicAopProxy
基于JDK的Proxy <-- JdkDynamicAopProxy : 创建
Advised <|-- 基于JDK的Proxy
class AopProxy {
+getProxy() Object
}
class ProxyFactory {
proxyTargetClass : boolean
}
class ObjenesisCglibAopProxy {
advised : ProxyFactory
}
class JdkDynamicAopProxy {
advised : ProxyFactory
}
<<interface>> Advised
<<interface>> AopProxyFactory
<<interface>> AopProxy
AopProxyFactory
根据 proxyTargetClass
等设置选择 AopProxy
实现,AopProxy
通过 getProxy()
方法创建代理对象。
上述类图中的类与接口都实现了 Advised
接口,能够获得关联的切面集合与目标(实际上是从 ProxyFactory
中获取的)。
调用代理方法时,会借助 ProxyFactory
统一将通知转换为环绕通知 MethodInterceptor
。
16. 切点匹配
上一节中,选择 AspectJExpressionPointcut
作为切点的实现,判断编写的 AspectJ 表达式是否与某一方法匹配可以使用其 matches()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main (String[] args) throws NoSuchMethodException { AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut (); pt1.setExpression("execution(* bar())" ); System.out.println(pt1.matches(T1.class.getMethod("foo" ), T1.class)); System.out.println(pt1.matches(T1.class.getMethod("bar" ), T1.class)); AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut (); pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)" ); System.out.println(pt2.matches(T1.class.getMethod("foo" ), T1.class)); System.out.println(pt2.matches(T1.class.getMethod("bar" ), T1.class)); } static class T1 { @Transactional public void foo () { } public void bar () { } }
运行 main()
方法后,控制台打印出:
false
true
true
false
@Transactional
是 Spring 中使用频率非常高的注解,那它底层是通过 AspectJExpressionPointcut
与 @annotation()
切点表达式相结合对目标方法进行匹配的吗?
答案是否定的。@Transactional
注解除了可以作用在方法上,还可以作用在类(或接口)上。
在底层 @Transactional
注解的匹配使用到了 StaticMethodMatcherPointcut
,在此模拟一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public static void main (String[] args) throws NoSuchMethodException { StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut () { @Override public boolean matches (Method method, Class<?> targetClass) { MergedAnnotations annotations = MergedAnnotations.from(method); if (annotations.isPresent(Transactional.class)) { return true ; } annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY); return annotations.isPresent(Transactional.class); } }; System.out.println(pt3.matches(T1.class.getMethod("foo" ), T1.class)); System.out.println(pt3.matches(T1.class.getMethod("bar" ), T1.class)); System.out.println(pt3.matches(T2.class.getMethod("foo" ), T2.class)); System.out.println(pt3.matches(T3.class.getMethod("foo" ), T3.class)); } static class T1 { @Transactional public void foo () { } public void bar () { } } @Transactional static class T2 { public void foo () { } } @Transactional interface I3 { void foo () ; } static class T3 implements I3 { @Override public void foo () { } }
运行 main()
方法后,控制台打印出:
true
false
true
true
无论是 AspectJExpressionPointcut
还是 StaticMethodMatcherPointcut
,它们都实现了 MethodMatcher
接口,用来执行方法的匹配。
17. 从 @Aspect 到 Advisor
17.1 AnnotationAwareAspectJAutoProxyCreator
讲解之前,准备一下类:
两个目标类
一个使用 @Aspect
的高级切面
一个利用配置类实现的低级切面 Advisor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 static class Target1 { public void foo () { System.out.println("target1 foo" ); } } static class Target2 { public void bar () { System.out.println("target2 bar" ); } } @Aspect static class Aspect1 { @Before("execution(* foo())") public void before () { System.out.println("aspect1 before..." ); } @After("execution(* foo())") public void after () { System.out.println("aspect1 after..." ); } } @Configuration static class Config { @Bean public Advisor advisor3 (MethodInterceptor advice3) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression("execution(* foo())" ); return new DefaultPointcutAdvisor (pointcut, advice3); } @Bean public MethodInterceptor advices () { return invocation -> { System.out.println("advice3 before..." ); Object result = invocation.proceed(); System.out.println("advice3 after..." ); return result; }; } }
编写 main()
方法创建 Spring 容器,并添加必要的 Bean:
1 2 3 4 5 6 7 8 9 10 11 12 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("aspect1" , Aspect1.class); context.registerBean("config" , Config.class); context.registerBean(ConfigurationClassPostProcessor.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); }
运行 main()
方法后,控制台打印出:
aspect1
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
advisor3
advices
Spring 中存在一个名为 AnnotationAwareAspectJAutoProxyCreator
的 Bean 后置处理器,尽管它的名称中没有 BeanPostProcessor
的字样,但它确实是实现了 BeanPostProcessor
接口的。
AnnotationAwareAspectJAutoProxyCreator
有两个主要作用:
找到容器中所有的切面,针对高级切面,将其转换为低级切面;
根据切面信息,利用 ProxyFactory
创建代理对象。
AnnotationAwareAspectJAutoProxyCreator
实现了 BeanPostProcessor
,可以在 Bean 生命周期中的一些阶段对 Bean 进行拓展。AnnotationAwareAspectJAutoProxyCreator
可以在 Bean 进行 依赖注入之前 、Bean 初始化之后 对 Bean 进行拓展。
重点介绍 AnnotationAwareAspectJAutoProxyCreator
中的两个方法:
findEligibleAdvisors()
:位于父类 AbstractAdvisorAutoProxyCreator
中,用于找到符合条件的切面类。低级切面直接添加,高级切面转换为低级切面再添加。
wrapIfNecessary()
:位于父类 AbstractAutoProxyCreator
中,用于将有资格被代理的 Bean 进行包装,即创建代理对象。
findEligibleAdvisors()
方法
findEligibleAdvisors()
方法接收两个参数:
beanClass
:配合切面使用的目标类 Class 信息
beanName
:当前被代理的 Bean 的名称
修改 main()
方法,向容器中添加 AnnotationAwareAspectJAutoProxyCreator
后置处理器,测试 findEligibleAdvisors()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("aspect1" , Aspect1.class); context.registerBean("config" , Config.class); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class); context.refresh(); AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class); List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1" ); advisors.forEach(System.out::println); context.close(); }
运行 main()
方法后,控制台打印出:
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.framework.autoproxy.A17$Config$$Lambda$56/802243390@7bd4937b]
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before()]; perClauseKind=SINGLETON
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.after()]; perClauseKind=SINGLETON
打印出 4 个能配合 Target1
使用的切面信息,其中:
第一个切面 ExposeInvocationInterceptor.ADVISOR
是 Spring 为每个代理对象都会添加的切面;
第二个切面 DefaultPointcutAdvisor
是自行编写的低级切面;
第三个和第四个切面 InstantiationModelAwarePointcutAdvisor
是由高级切面转换得到的两个低级切面。
若按照 creator.findEligibleAdvisors(Target2.class, "target2")
的方式进行调用,控制台不会打印出任何信息,因为没有任何切面能够配合 Target2
使用。
wrapIfNecessary()
方法
wrapIfNecessary()
方法内部调用了 findEligibleAdvisors()
方法,若 findEligibleAdvisors()
方法返回的集合不为空,则表示需要创建代理对象。
如果需要创建对象,wrapIfNecessary()
方法返回的是代理对象,否则仍然是原对象。
wrapIfNecessary()
方法接收三个参数:
bean
:原始 Bean 实例
beanName
:Bean 的名称
cacheKey
:用于元数据访问的缓存 key
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) { Object o1 = creator.wrapIfNecessary(new Target1 (), "target1" , "target1" ); System.out.println(o1.getClass()); Object o2 = creator.wrapIfNecessary(new Target2 (), "target2" , "target2" ); System.out.println(o2.getClass()); context.close(); }
运行 main()
方法后,控制台打印出:
class org.springframework.aop.framework.autoproxy.A17$Target1$$EnhancerBySpringCGLIB$$634976f6
class org.springframework.aop.framework.autoproxy.A17$Target2
Target1
对象是被代理的,而 Target2
依旧是原对象。
如果将 o1
转换为 Target1
,并调用 foo()
方法,foo()
方法将被增强:
1 2 3 4 5 6 7 public static void main (String[] args) { ((Target1) o1).foo(); context.close(); }
advice3 before...
aspect1 before...
target1 foo
aspect1 after...
advice3 after...
切面的顺序控制
根据上述打印的信息可知,低级切面相比于高级切面先一步被执行,这个执行顺序是可以被控制的。
针对高级切面来说,可以在类上使用 @Order
注解,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Aspect @Order(1) static class Aspect1 { @Before("execution(* foo())") public void before () { System.out.println("aspect1 before..." ); } @After("execution(* foo())") public void after () { System.out.println("aspect1 after..." ); } }
在高级切面中,@Order
只有放在类上才生效,放在方法上不会生效。比如高级切面中有多个前置通知,这些前置通知对应的方法上使用 @Order
注解是无法生效的。
针对低级切面,需要设置 advisor
的 order
值,而不是向高级切面那样使用 @Order
注解,使用 @Order
注解设置在 advisor3()
方法上是无用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Configuration static class Config { @Bean public Advisor advisor3 (MethodInterceptor advice3) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression("execution(* foo())" ); DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor (pointcut, advice3); advisor.setOrder(2 ); return advisor; } }
设置完成后,高级切面的执行优先级高于低级切面。执行 main()
方法验证执行顺序是否改变:
aspect1 before...
advice3 before...
target1 foo
advice3 after...
aspect1 after...
17.2 代理对象创建时机
使用 AnnotationAwareAspectJAutoProxyCreator
Bean 后置处理器创建代理对象的时机有以下两个选择:
Bean 的依赖注入之前
Bean 初始化完成之后
这两个时机二选一,不会重复创建代理对象。
以下述代码为例,查看代理对象的创建时机:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 package org.springframework.aop.framework.autoproxy;public class A17_1 { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(Config.class); context.refresh(); context.close(); } @Configuration static class Config { @Bean public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator () { return new AnnotationAwareAspectJAutoProxyCreator (); } @Bean public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor () { return new AutowiredAnnotationBeanPostProcessor (); } @Bean public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor () { return new CommonAnnotationBeanPostProcessor (); } @Bean public Advisor advisor (MethodInterceptor advice) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression("execution(* foo())" ); return new DefaultPointcutAdvisor (pointcut, advice); } @Bean public MethodInterceptor advice () { return invocation -> { System.out.println("before..." ); return invocation.proceed(); }; } @Bean public Bean1 bean1 () { return new Bean1 (); } @Bean public Bean2 bean2 () { return new Bean2 (); } } static class Bean1 { public void foo () {} public Bean1 () { System.out.println("Bean1()" ); } @PostConstruct public void init () { System.out.println("Bean1 init()" ); } } static class Bean2 { public Bean2 () { System.out.println("Bean2()" ); } @Autowired public void setBean1 (Bean1 bean1) { System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass()); } @PostConstruct public void init () { System.out.println("Bean2 init()" ); } } }
其中 bean2
中注入了 bean1
。运行 main()
方法后,控制台打印出:
Bean1()
Bean1 init()
Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2()
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$b7d6405
Bean2 init()
在 bean1
初始化完成后,额外打印了一句日志信息:
Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
表示为 bean1
创建了隐式代理。
此时代理对象在 Bean 初始化完成之后创建。
之后为 bean2
进行依赖注入时,注入的 bean1
是代理对象。
在 Bean1
类中添加 setBean2()
方法,表示向 bean1
中注入 bean2
,此时 bean1
依赖 bean2
,而 bean2
原本就依赖了 bean1
,出现循环依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static class Bean1 { public void foo () {} public Bean1 () { System.out.println("Bean1()" ); } @Autowired public void setBean2 (Bean2 bean2) { System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass()); } @PostConstruct public void init () { System.out.println("Bean1 init()" ); } }
再次运行 main()
方法,查看 bean1
的代理对象的生成时机:
Bean1()
Bean2()
Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$5cff48bf
Bean2 init()
Bean1 setBean2(bean2) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean2
Bean1 init()
首先进行 bean1
的实例化,然后进行 bean1
的依赖注入,但此时容器中并没有 bean2
,因此需要进行 bean2
的实例化。
接下来进行 bean2
的依赖注入,向 bean2
中注入 bean1
,注入的 bean1
应该是被增强的,即它的代理对象,因此创建 bean1
的代理对象后再完成 bean2
的依赖注入。
接着继续 bean2
的生命周期,完成 bean2
的初始化阶段,最后回到 bean1
的依赖注入阶段,向 bean1
中注入 bean2
,最后完成 bean1
的初始化阶段。
总结
代理对象的创建时机:
无循环依赖时,在 Bean 初始化阶段之后创建;
有循环依赖时,在 Bean 实例化后、依赖注入之前创建,并将代理对象暂存于二级缓存。
Bean 的依赖注入阶段和初始化阶段不应该被增强,仍应被施加于原始对象。
17.3 高级切面转低级切面
调用 AnnotationAwareAspectJAutoProxyCreator
对象的 findEligibleAdvisors()
方法时,获取能配合目标 Class 使用的切面,最终返回 Advisor
列表。在搜索过程中,如果遇到高级切面,则会将其转换成低级切面。
现有切面类与目标类信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 static class Aspect { @Before("execution(* foo())") public void before1 () { System.out.println("before1" ); } @Before("execution(* foo())") public void before2 () { System.out.println("before2" ); } public void after () { System.out.println("after" ); } public void afterReturning () { System.out.println("afterReturning" ); } public void afterThrowing () { System.out.println("afterThrowing" ); } public Object around (ProceedingJoinPoint pjp) throws Throwable { try { System.out.println("around...before" ); return pjp.proceed(); } finally { System.out.println("around...after" ); } } } static class Target { public void foo () { System.out.println("target foo" ); } }
高级切面中与通知类型相关的常用注解有 5 个:
@Before
:前置通知
@AfterReturning
:后置通知
@AfterThrowing
:异常通知
@After
:最终通知
@Around
:环绕通知
以解析 @Before
注解为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main (String[] args) throws Throwable { AspectInstanceFactory factory = new SingletonAspectInstanceFactory (new Aspect ()); List<Advisor> list = new ArrayList <>(); for (Method method : Aspect.class.getDeclaredMethods()) { if (method.isAnnotationPresent(Before.class)) { String expression = method.getAnnotation(Before.class).value(); AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression(expression); AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice (method, pointcut, factory); Advisor advisor = new DefaultPointcutAdvisor (pointcut, advice); list.add(advisor); } } for (Advisor advisor : list) { System.out.println(advisor); } }
运行 main()
方法,控制台打印出:
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.autoproxy.A17_2$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.autoproxy.A17_2$Aspect.before1()]; aspect name '']
@Before
标记的前置通知会被转换成原始的 AspectJMethodBeforeAdvice
形式,该对象包含了以下信息:
通知对应的方法信息
切点信息
通知对象如何创建,本例公用一个 Aspect
对象
通知相关注解与原始通知类对应关系如下:
注解
对应的原始通知类
@Before
AspectJMethodBeforeAdvice
@AfterReturning
AspectJAfterReturningAdvice
@AfterThrowing
AspectJAfterThrowingAdvice
@After
AspectJAfterAdvice
@Around
AspectJAroundAdvice
18. 静态通知调用
18.1 统一转换成环绕通知
通知相关注解都对应一个原始通知类,在 Spring 底层会将这些通知转换成环绕通知 MethodInterceptor
。如果原始通知类本就实现了 MethodInterceptor
接口,则无需转换。
原始通知类
是否需要转换成 MethodInterceptor
AspectJMethodBeforeAdvice
✅
AspectJAfterReturningAdvice
✅
AspectJAfterThrowingAdvice
❌
AspectJAfterAdvice
❌
AspectJAroundAdvice
❌
使用 ProxyFactory
无论基于哪种方式创建代理对象,最终调用 advice(通知,或者说通知对应的方法)的都是 MethodInvocation
对象。
项目中存在的 advisor(原本的低级切面和由高级切面转换得到的低级切面)往往不止一个,它们一个套一个地被调用,因此需要一个调用链对象,即 MethodInvocation
。
MethodInvocation
需要知道 advice 有哪些,还需要知道目标对象是哪个。调用次序如下:
由上图可知,环绕 通知最适合作为 advice,而 Before、AfterReturning 都应该转换成环绕通知。
统一转换成环绕通知的形式,体现了设计模式中的适配器模式:
对外更方便使用和区分各种通知类型
对内统一都是环绕通知,统一使用 MethodInterceptor
表示
通过 ProxyFactory
对象的 getInterceptorsAndDynamicInterceptionAdvice()
方法将其他通知统一转换为 MethodInterceptor
环绕通知:
注解
原始通知类
适配器
拦截器
@Before
AspectJMethodBeforeAdvice
MethodBeforeAdviceAdapter
MethodBeforeAdviceInterceptor
@AfterReturning
AspectJAfterReturningAdvice
AspectJAfterReturningAdvice
AfterReturningAdviceInterceptor
转换得到的通知都是静态通知,体现在 getInterceptorsAndDynamicInterceptionAdvice()
方法中的 Interceptors
部分,这些通知在被调用时无需再次检查切点,直接调用即可。
代码测试
切面类与目标类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 static class Aspect { @Before("execution(* foo())") public void before1 () { System.out.println("before1" ); } @Before("execution(* foo())") public void before2 () { System.out.println("before2" ); } public void after () { System.out.println("after" ); } @AfterReturning("execution(* foo())") public void afterReturning () { System.out.println("afterReturning" ); } @AfterThrowing("execution(* foo())") public void afterThrowing (Exception e) { System.out.println("afterThrowing " + e.getMessage()); } @Around("execution(* foo())") public Object around (ProceedingJoinPoint pjp) throws Throwable { try { System.out.println("around...before" ); return pjp.proceed(); } finally { System.out.println("around...after" ); } } } static class Target { public void foo () { System.out.println("target foo" ); } }
将高级切面转换成低级切面,并将通知统一转换成环绕通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 @SuppressWarnings("all") public static void main (String[] args) throws Throwable { AspectInstanceFactory factory = new SingletonAspectInstanceFactory (new Aspect ()); List<Advisor> list = new ArrayList <>(); for (Method method : Aspect.class.getDeclaredMethods()) { if (method.isAnnotationPresent(Before.class)) { String expression = method.getAnnotation(Before.class).value(); AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression(expression); AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice (method, pointcut, factory); Advisor advisor = new DefaultPointcutAdvisor (pointcut, advice); list.add(advisor); } else if (method.isAnnotationPresent(AfterReturning.class)) { String expression = method.getAnnotation(AfterReturning.class).value(); AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression(expression); AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice (method, pointcut, factory); Advisor advisor = new DefaultPointcutAdvisor (pointcut, advice); list.add(advisor); } else if (method.isAnnotationPresent(Around.class)) { String expression = method.getAnnotation(Around.class).value(); AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut (); pointcut.setExpression(expression); AspectJAroundAdvice advice = new AspectJAroundAdvice (method, pointcut, factory); Advisor advisor = new DefaultPointcutAdvisor (pointcut, advice); list.add(advisor); } } for (Advisor advisor : list) { System.out.println(advisor); } Target target = new Target (); ProxyFactory proxyFactory = new ProxyFactory (); proxyFactory.setTarget(target); proxyFactory.addAdvisors(list); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" ); List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo" ), Target.class); for (Object o : methodInterceptorList) { System.out.println(o); } }
运行 main()
方法后,控制台打印出:
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.afterReturning()]; aspect name '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@7ce6a65d
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@1500955a
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@e874448
根据打印信息可知:
前置通知 AspectJMethodBeforeAdvice
被转换成 MethodBeforeAdviceInterceptor
环绕通知 AspectJAroundAdvice
保持不变
后置通知 AspectJAfterReturningAdvice
被转换成 AfterReturningAdviceInterceptor
18.2 调用链执行
高级切面成功转换成低级切面,切面中的通知也全部转换成环绕通知 MethodInterceptor
,最后还要调用这些通知和目标方法。
这个调用交由调用链对象 MethodInvocation
来完成,在调用链对象中存放了所有经过转换得到的环绕通知和目标方法。
MethodInvocation
是一个接口,其最根本的实现是 ReflectiveMethodInvocation
。
构建 ReflectiveMethodInvocation
对象需要 6 个参数:
proxy
:代理对象
target
:目标对象
method
:目标对象中的方法对象
arguments
:调用目标对象中的方法需要的参数
targetClass
:目标对象的 Class 对象
interceptorsAndDynamicMethodMatchers
:转换得到的环绕通知列表
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) throws Throwable { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" ); MethodInvocation methodInvocation = new ReflectiveMethodInvocation ( null , target, Target.class.getMethod("foo" ), new Object [0 ], Target.class, methodInterceptorList ); methodInvocation.proceed(); }
运行 main()
方法后会抛出异常:
Exception in thread "main" java.lang.IllegalStateException: No MethodInvocation found:
提示没有找到 MethodInvocation
。但调用链对象不是已经创建好了吗?
这是因为调用链在执行过程会调用到很多通知,而某些通知内部可能需要使用调用链对象。因此需要将调用链对象存放在某一位置,使所有通知都能获取到调用链对象。
这个“位置”就是 当前线程 。
那怎么将调用链对象放入当前线程呢?
可以在所有通知的最外层再添加一个环绕通知,其作用是将调用链对象放入当前线程。
可以使用 Spring 提供的 ExposeInvocationInterceptor
作为最外层的环绕通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void main (String[] args) throws Throwable { Target target = new Target (); ProxyFactory proxyFactory = new ProxyFactory (); proxyFactory.setTarget(target); proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); proxyFactory.addAdvisors(list); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" ); MethodInvocation methodInvocation = new ReflectiveMethodInvocation ( null , target, Target.class.getMethod("foo" ), new Object [0 ], Target.class, methodInterceptorList ); methodInvocation.proceed(); }
再次运行 main()
方法不再报错,控制台打印出:
before1
around...before
before2
target foo
around...after
afterReturning
18.3 模拟实现调用链
调用链执行过程是一个递归过程。执行 proceed()
方法将调用调用链中下一个通知或目标方法。当调用链中没有通知时,就调用目标方法,反之调用下一个通知。
这体现了设计模式中的责任链模式。
目标类 Target
:
1 2 3 4 5 static class Target { public void foo () { System.out.println("Target foo()" ); } }
实现 MethodInterceptor
接口,编写两个环绕通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static class Advice1 implements MethodInterceptor { @Override public Object invoke (MethodInvocation invocation) throws Throwable { System.out.println("Advice1.before()" ); Object result = invocation.proceed(); System.out.println("Advice1.after()" ); return result; } } static class Advice2 implements MethodInterceptor { @Override public Object invoke (MethodInvocation invocation) throws Throwable { System.out.println("Advice2.before()" ); Object result = invocation.proceed(); System.out.println("Advice2.after()" ); return result; } }
实现 MethodInvocation
接口,实现自己的调用链:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 static class MyInvocation implements MethodInvocation { private final Object target; private final Method method; private final Object[] args; private final List<MethodInterceptor> methodInterceptorList; private int count = 1 ; public MyInvocation (Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) { this .target = target; this .method = method; this .args = args; this .methodInterceptorList = methodInterceptorList; } @Override public Method getMethod () { return this .method; } @Override public Object[] getArguments() { return this .args; } @Override public Object proceed () throws Throwable { if (count > methodInterceptorList.size()) { return method.invoke(target, args); } MethodInterceptor interceptor = methodInterceptorList.get(count++ - 1 ); return interceptor.invoke(this ); } @Override public Object getThis () { return this .target; } @Override public AccessibleObject getStaticPart () { return method; } }
编写 main()
方法,执行调用链,查看控制台输出结果:
1 2 3 4 5 6 7 8 9 public static void main (String[] args) throws Throwable { Target target = new Target (); List<MethodInterceptor> list = new ArrayList <>(Arrays.asList( new Advice1 (), new Advice2 () )); MyInvocation invocation = new MyInvocation (target, Target.class.getMethod("foo" ), new Object [0 ], list); invocation.proceed(); }
Advice1.before()
Advice2.before()
Target foo()
Advice2.after()
Advice1.after()
18.4 代理对象调用流程
以 JDK 动态代理实现为例:
从 ProxyFactory
获得 Target
和环绕通知链,根据它们创建 MethodInvocation
对象,简称 mi
首次执行 mi.proceed()
后发现有下一个环绕通知,调用它的 invoke(mi)
进入环绕通知 1,执行前增强,再次调用 mi.proceed()
后又发现有下一个环绕通知,调用它的 invoke(mi)
进入环绕通知 2,执行前增强,调用 mi.proceed()
发现没有环绕通知,调用 mi.invokeJoinPoint()
执行目标方法
目标方法执行结束,将结果返回给环绕通知 2,执行环绕通知 2 的后增强
环绕通知 2 继续将结果返回给环绕通知 1,执行环绕通知 1 的后增强
环绕通知 1 返回最终的结果
下图中不同颜色对应一次环绕通知或目标的调用起始至终结:
sequenceDiagram
participant Proxy
participant ih as InvocationHandler
participant mi as MethodInvocation
participant Factory as ProxyFactory
participant mi1 as MethodInterceptor1
participant mi2 as MethodInterceptor2
participant Target
Proxy ->> +ih : invoke()
ih ->> +Factory : 获得 Target
Factory -->> -ih : 返回 Target
ih ->> +Factory : 获得 MethodInterceptor 链
Factory -->> -ih : 返回 MethodInterceptor 链
ih ->> +mi : 创建 mi
mi -->> -ih : 返回 mi
rect rgb(200, 223, 255)
ih ->> +mi : mi.proceed()
mi ->> +mi1 : invoke(mi)
mi1 ->> mi1 : 前增强
rect rgb(200, 190, 255)
mi1 ->> mi : mi.proceed()
mi ->> +mi2 : invoke(mi)
mi2 ->> mi2 : 前增强
rect rgb(150, 190, 155)
mi2 ->> mi : mi.proceed()
mi ->> +Target : mi.invokeJoinPoint()
Target ->> Target : 调用目标方法
Target -->> -mi2 : 结果
end
mi2 ->> mi2 : 后增强
mi2 -->> -mi1 : 结果
end
mi1 ->> mi1 : 后增强
mi1 -->> -mi : 结果
mi -->> -ih : 结果
end
ih -->> -Proxy : 结果
19. 动态通知调用
前文的示例都是静态通知调用,无需参数绑定,执行时无需切点信息,性能较高。
相应地就有动态通知调用,它需要参数绑定,执行时还需要切点信息,性能较低。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Aspect static class MyAspect { @Before("execution(* foo(..))") public void before1 () { System.out.println("before1" ); } @Before("execution(* foo(..)) && args(x)") public void before2 (int x) { System.out.printf("before(%d)\n" , x); } }
目标类 Target
:
1 2 3 4 5 static class Target { public void foo (int x) { System.out.printf("target foo(%d)\n" , x); } }
配置类 MyConfig
:
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration static class MyConfig { @Bean public AnnotationAwareAspectJAutoProxyCreator proxyCreator () { return new AnnotationAwareAspectJAutoProxyCreator (); } @Bean public MyAspect myAspect () { return new MyAspect (); } }
编写 main()
方法,新建 Spring 容器,查找符合条件的切面,将所有通知转换成环绕通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) throws Throwable { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(MyConfig.class); context.refresh(); AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class); List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target" ); Target target = new Target (); ProxyFactory factory = new ProxyFactory (); factory.setTarget(target); factory.addAdvisors(list); List<Object> interceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo" , int .class), Target.class); for (Object o : interceptorList) { System.out.println(o); } }
执行 main()
方法,控制台打印出:
org.springframework.aop.interceptor.ExposeInvocationInterceptor@73e22a3d
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@47faa49c
org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher@28f2a10f
第一个 ExposeInvocationInterceptor
对象是 Spring 添加的环绕通知,第二个 MethodBeforeAdviceInterceptor
对象是前置通知转换得到的环绕通知,那 InterceptorAndDynamicMethodMatcher
对象是什么呢?
1 2 3 4 5 6 7 8 9 10 11 12 class InterceptorAndDynamicMethodMatcher { final MethodInterceptor interceptor; final MethodMatcher methodMatcher; public InterceptorAndDynamicMethodMatcher (MethodInterceptor interceptor, MethodMatcher methodMatcher) { this .interceptor = interceptor; this .methodMatcher = methodMatcher; } }
InterceptorAndDynamicMethodMatcher
并没有实现 MethodInterceptor
接口,它 不是一个环绕通知 ,对应了动态通知调用。
因此 ProxyFactory
对象的 getInterceptorsAndDynamicInterceptionAdvice()
方法返回的不仅是转换得到的环绕通知,还有对应动态通知调用的 InterceptorAndDynamicMethodMatcher
对象。
InterceptorAndDynamicMethodMatcher
对象中包含了环绕通知 interceptor
对象和切点信息 methodMatcher
(前文使用过的 AspectJExpressionPointcut
也实现了 MethodMatcher
接口)。
尝试查看 InterceptorAndDynamicMethodMatcher
对象中包含的信息,但该类并未声明成 public
,其成员变量也未被 public
修饰,也没提供获取的方式,但可以使用反射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void main (String[] args) throws Throwable { for (Object o : interceptorList) { showDetail(o); } } public static void showDetail (Object o) { try { Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher" ); if (clazz.isInstance(o)) { Field methodMatcher = clazz.getDeclaredField("methodMatcher" ); methodMatcher.setAccessible(true ); Field methodInterceptor = clazz.getDeclaredField("interceptor" ); methodInterceptor.setAccessible(true ); System.out.println("环绕通知和切点:" + o); System.out.println("\t切点为:" + methodMatcher.get(o)); System.out.println("\t通知为:" + methodInterceptor.get(o)); } else { System.out.println("普通环绕通知:" + o); } } catch (Exception e) { throw new RuntimeException (e); } }
运行 main()
方法后,控制台打印出:
普通环绕通知:org.springframework.aop.interceptor.ExposeInvocationInterceptor@73e22a3d
普通环绕通知:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@47faa49c
环绕通知和切点:org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher@f736069
切点为:AspectJExpressionPointcut: (int x) execution(* foo(..)) && args(x)
通知为:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@6da21078
根据打印的切点信息可知,InterceptorAndDynamicMethodMatcher
对象的确对应了动态通知调用。
最后创建调用链对象,执行通知和原始方法:
1 2 3 4 5 6 7 8 9 10 11 12 public static void main (String[] args) throws Throwable { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>" ); Object proxy = factory.getProxy(); MethodInvocation methodInvocation = new ReflectiveMethodInvocation ( proxy, target, Target.class.getMethod("foo" , int .class), new Object []{100 }, Target.class, interceptorList ) { }; methodInvocation.proceed(); }
>>>>>>>>>>>>>>>>>>>>>>>>>>
before1
before(100)
target foo(100)
动态通知调用需要切点信息,需要对参数进行匹配和绑定,复杂程度高,性能比静态通知调用低。