ajc编译器
使用aspectj编译器插件来改动class类文件来实现增强(在编译阶段)
编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</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>
|
因为idea 不会调用 ajc 编译器,所以一定要用 maven 的 compile 来编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @SpringBootApplication public class A09 { private static final Logger log = LoggerFactory.getLogger(A09.class); public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A09.class, args); MyService service = context.getBean(MyService.class); log.debug("service class: {}", service.getClass()); service.foo(); context.close(); } }
|
agent 类加载
运行时需要在 VM options 里加入-javaagent:(maven仓库地址)/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
类加载时修改 .class 文件实现增强
Proxy增强(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
| public class JdkProxyDemo {
interface Foo { void foo(); }
static final class Target implements Foo { public void foo() { System.out.println("target foo"); } }
public static void main(String[] param) throws IOException { Target target = new Target(); ClassLoader loader = JdkProxyDemo.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before..."); Object res = method.invoke(target, args); System.out.println("after..."); return res; } }); proxy.foo(); } }
|
总结
代理对象和目标对象都实现了Foo
接口,是兄弟关系。代理对象类型不能强转成目标对象类型
目标类定义的时候可以加final
修饰
原理
1、先用代码简单模拟一下JDK动态代理。先定义一个Foo接口,里面有一个foo方法,再定义一个Target类来实现这个接口,代码如下:
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
| public interface Foo { void foo(); }
@Slf4j public final class Target implements Foo { public void foo() { log.debug("target foo"); } }
public class $Proxy0 implements Foo { @Override public void foo() { System.out.println("before..."); new Target().foo(); } }
public class Main { public static void main(String[] args) { Foo proxy = new $Proxy0(); proxy.foo(); } }
|
接下来对Target
类中的foo()
方法进行增强
首先想再定义一个类实现Foo
接口,然后在foo()
中编写增强代码,接着再new
一个Target
对象,调用它的foo()
方法,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class $Proxy0 implements Foo { @Override public void foo() { System.out.println("before..."); new Target().foo(); } }
public class Main { public static void main(String[] args) { Foo proxy = new $Proxy0(); proxy.foo(); } }
|
2、上面的代码把功能增强的代码和调用目标的代码都固定在了代理类的内部,不太灵活。
因此可以通过定义一个InvocationHandler
接口的方式来将这部分代码解耦出来,代码如下:
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 interface InvocationHandler { void invoke(); } public interface Foo { void foo(); } @Slf4j public final class Target implements Foo { public void foo() { System.out.println("target foo"); } } public class Main { public static void main(String[] args) { Foo proxy = new $Proxy0(new InvocationHandler() { @Override public void invoke() { System.out.println("before..."); new Target().foo(); } }); proxy.foo(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class $Proxy0 implements Foo { private InvocationHandler h;
public $Proxy0(InvocationHandler h) { this.h = h; }
@Override public void foo() { h.invoke(); } }
|
3、虽然将功能增强的代码和调用目标的代码通过接口的方式独立出来了,但是如果此时接口中新增了一个方法bar()
,Target类和$Proxy0
类中都要实现bar()方法,那么调用proxy的foo()
和bar()
方法都将间接调用目标对象的foo()方法,因为在InvocationHandler
的invoke()
方法中调用的是target.foo()
方法
4、改进为代理类中调用方法的时候,通过反射把接口中对应的方法Method
对象作为参数传给InvocationHandler
,这样就可以通过保证调用proxy调用方法时一定能在invoke中调用对应的方法
5、同时,修改Foo接口的中的bar()
方法,使其具有int
类型的返回值,因此InvocationHandler
的invoke()
方法也得有返回值,同时将代理对象本身作为第一个参数,具体代码如下:
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
| public class A12 {
interface Foo { void foo(); int bar(); }
static class Target implements Foo { public void foo() { System.out.println("target foo"); }
@Override public int bar() { System.out.println("target bar"); return 100; } }
public static void main(String[] param) throws NoSuchMethodException { Foo proxy = new $Proxy0(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { System.out.println("before"); return method.invoke(new Target(),args); } }); proxy.foo(); proxy.bar(); } }
|
当每次调用foo或者bar方法时,多次加载。此处使用静态代码块对foo和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 46 47 48 49 50 51
|
public class $Proxy0 extends Proxy implements Foo {
public $Proxy0(InvocationHandler h){ super(h); }
@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 { Object res = h.invoke(this,bar, new Object[0]); return (int) res; } catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } static Method foo; static Method bar; static { try { foo = Foo.class.getMethod("foo"); bar = Foo.class.getMethod("bar"); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } }
|
反射优化
前 16 次是调用了java本地的MethodAccessor 的实现类,反射性能较低
第 17 次调用会生成代理类,优化为非反射调用(用 arthas 的 jad 工具反编译第 17 次调用生成的代理类)
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
| public class TestMethodInvoke { public static void main(String[] args) throws Exception { Method foo = TestMethodInvoke.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("jdk.internal.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"); } }
|
Proxy增强(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
| public class CglibProxyDemo {
static class Target {` public void foo() { System.out.println("target foo"); } }
public static void main(String[] param) { Target target = new Target(); Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() { @Override public Object intercept (Object p, Method method, Object[] args, MethodProxy methodProxy)throws Throwable { System.out.println("before...");
Object res = methodProxy.invokeSuper(p, args); System.out.println("after..."); return res; } }); proxy.foo(); } }
|
总结
1
| public Object intercept (Object p, Method method, Object[] args, MethodProxy methodProxy)
|
MethodInterceptor
的intercept()
方法的第2个参数是method,可以通过反射对目标方法进行调用
1
| Object result = method.invoke(target, args);
|
第4个参数proxy,可以不用反射就能对目标方法进行调用
1 2
| Object result = proxy.invoke(target, args); Object result = proxy.invokeSuper(obj, args);
|
代理类不需要实现接口
代理对象和目标对象是父子关系,代理类继承于目标类;
目标类定义的时候不能加final修饰,否则代理类就无法继承目标类了,会报IllegalArgumentException
目标类方法定义的时候不能加final修饰,否则代理类继承目标类以后就不能重写目标类的方法了
原理
代码模拟cglib动态代理
先创建一个目标类,其中有三个方法,再创建一个代理类继承目标类,并且通过构造方法传入MethodInterceptor对象用于回调。重写目标类后,通过调用intercept方法实现功能增强(静态代码块作用与jdk动态代理中类似)
1 2 3 4 5
| public class Target { public void save() {System.out.println("save()");} public void save(int i) {System.out.println("save(int)");} public void save(long j) {System.out.println("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
| public class Proxy extends Target { private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) { this.methodInterceptor = methodInterceptor; }
@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 j){ try { methodInterceptor.intercept(this,save2,new Object[]{j}, null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } }
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()); }
} }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static void main(String[] args) { Proxy proxy = new Proxy(); Target target = new Target(); proxy.setMethodInterceptor(new MethodInterceptor() { @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before..."); Object res = method.invoke(target, objects); return res; } }); proxy.save(); proxy.save(2); proxy.save(5L); }
|
创建MethodProxy
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
| static MethodProxy save0Proxy; static MethodProxy save1Proxy; static MethodProxy save2Proxy; static { 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"); }
public void saveSuper() {super.save();} public void saveSuper(int i) {super.save(i);} public void saveSuper(long j) {super.save(j);}
@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 j){ try { methodInterceptor.intercept(this,save2,new Object[]{j}, save2Proxy); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } }
|
cglib避免反射调用
MethodProxy在调用invoke和invokeSuper时不会走反射途经,而是通过FastClass来避免反射,cglib在调用方法时,生成两个代理类(为FastClass的子类),代理类中的方法能避免反射
调用invoke
在调用MethodProxy的create静态方法时,底层会创建一个FastClass子类(以下称代理类)来避免反射的调用(因为该代理类实现了fastClas太多功能,所以不直接继承FastClass类,而是选择其中两个功能进行实现)
1、当我们创建MethodProxy时,会传入signature(包括方法名字和参数返回值)
2、代理类中会根据传入的signature编写目标方法的编号(将传入的signature与自定义的signature进行比较)
3、代理类中根据获取方法的编号去调用方法
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
| 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; } else if (s1.equals(signature)) { return 1; } else if (s2.equals(signature)) { return 2; } else { return -1; } }
public Object invoke(int index,Object target,Object[] args) { if (index == 0) { ((Target)target).save(); return null; } else if (index == 1) { ((Target)target).save((int) args[0]); return null; } else if (index == 2) { ((Target) target).save((long) args[0]); return null; } else { throw new RuntimeException("无此方法"); } } }
|
调用invokeSuper
与上面调用invoke时步骤基本相同,唯一不同的是:
获取代理类中的方法编号时,应该获取的是带原始功能的方法了,而且在调用目标对象的方法时,也应该调用带原始功能的方法
原因:MethodProxy调用invokeSuper是在MethodInterceptor中执行的,增强功能已经在外面实现了,当获取到的是增强功能时,里面又间接调用了MethodInterceptor,进入循环调用
代码实现
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
| 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; } else if (s1.equals(signature)) { return 1; } else if (s2.equals(signature)) { return 2; } else { return -1; } }
public Object invoke(int index,Object proxy,Object[] args) { if (index == 0) { ((Proxy)proxy).saveSuper(); return null; } else if (index == 1) { ((Proxy)proxy).saveSuper((int) args[0]); return null; } else if (index == 2) { ((Proxy) proxy).saveSuper((long) args[0]); return null; } else { throw new RuntimeException("无此方法"); } }
public static void main(String[] args) { ProxyFastClass fastClass = new ProxyFastClass(); int index = fastClass.getIndex(new Signature("saveSuper", "()V")); System.out.println(index); fastClass.invoke(0, new Proxy(), new Object[0]); } }
|
对比
与jdk对比,jdk是调用17次时,针对一个方法产生一个代理类
cglib是MethodProxy调用的时候就产生代理,一个proxy代理类对应两个fastClass的子类
(一个是MethodProxy调用invoke时结合目标使用,一个是MethodProxy调用invokeSuper时结合代理使用)
Spring选择代理
切点:增强的匹配规则
通知:增强的逻辑
切面:切点和通知组合
两个切面的概念
aspect =
通知1(advice)+ 切点1(pointcut)
通知2(advice)+ 切点2(pointcut)
通知3(advice)+ 切点3(pointcut)
asvisor = 更细粒度的切面,只包含一个通知和切点
在aspect生效之前,会被拆解成多个advisor
接口及目标类实现
先创建一个包含又foo和bar方法的接口,再创建一个Target1目标类实现该接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| interface I1 { void foo(); void bar(); }
static class Target1 implements I1 { public void foo() { System.out.println("target1 foo"); } 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"); } }
|
准备切点
创建切点对象,调用setExpression设置切点表达式
1 2
| AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())");
|
准备通知
1 2 3 4 5 6 7 8 9
| MethodInterceptor advice = new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before..."); Object res = invocation.proceed(); System.out.println("after..."); return res; } };
|
准备切面
创建DefaultPointcutAdvisor
对象,传入切点和通知
然后再创建ProxyFactory
对象,添加目标对象和切面,最后通过代理对象调用方法完成测试
(ProxyFactory
内部会根据不同的情况选择cglib或者jdk的代理)
1 2 3 4 5 6 7 8 9
| DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
ProxyFactory factory = new ProxyFactory(); factory.setTarget(new Target1()); factory.addAdvisor(advisor);
I1 proxy = (I1) factory.getProxy(); proxy.foo(); proxy.bar();
|
选择代理的方式
**ProxyFactory
内部存在一个成员变量proxyTargetClass
决定spring的代理方式**
情况1
proxyTargetClass = false
,而且目标实现了接口, 使用 JdkDynamicAopProxy
实现(默认proxyTargetClass
为alse)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
Target1 target1 = new Target1(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target1); factory.addAdvisor(advisor);
factory.setInterfaces(target1.getClass().getInterfaces());
I1 proxy = (I1) factory.getProxy();
proxy.foo(); proxy.bar();
|
情况2
proxyTargetClass = false
,而且目标没有接口, 使用 ObjenesisCglibAopProxy
实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
Target1 target1 = new Target1(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target1); factory.addAdvisor(advisor);
I1 proxy = (I1) factory.getProxy();
proxy.foo(); proxy.bar();
|
情况3
proxyTargetClass = true
总是使用 ObjenesisCglibAopProxy
实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
Target1 target1 = new Target1(); ProxyFactory factory = new ProxyFactory(); factory.setTarget(target1); factory.addAdvisor(advisor);
factory.setProxyTargetClass(true);
I1 proxy = (I1) factory.getProxy();
proxy.foo(); proxy.bar();
|
切点匹配
先定义一个静态类T1,其中有用@Transactional注解的foo方法和一个普通的bar方法
再定义一个使用@Transactional注解标注的静态类T2
然后再定义一个用@Transactional注解标注的接口,内有方法foo,最后创建一个类实现该接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 { public void foo() { } } >}
|
1、创建AspectJExpressionPointcut对象,然后调用setExpression方法,在其中填写切点表达式(点我)
2、调用matches方法判断是否与切点表达式匹配,第一个参数为对应类的方法名,第二个参数为对应类的class
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class A16 { public static void main(String[] args) throws NoSuchMethodException { AspectJExpressionPointcut pointcut1 = new AspectJExpressionPointcut(); pointcut1.setExpression("execution(* bar())"); System.out.println(pointcut1.matches(T1.class.getMethod("foo"), T1.class)); System.out.println(pointcut1.matches(T1.class.getMethod("bar"), T1.class));
AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut(); pointcut2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)"); System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class)); System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class)); }
|
3、为了保证当出现T3类的情况时(实现了被注解标注的接口)@Transactional
注解能够被匹配到,在spring内部并不是采用以上方法匹配@Transactional
注解,而是创建StaticMethodMatcherPointcut对象并重写matches方法,在方法内部添加匹配注解的逻辑
spring探秘之组合注解的处理(点我)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| StaticMethodMatcherPointcut pointcut3 = 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); if (annotations.isPresent(Transactional.class)){ return true; } return false; } };
System.out.println(pointcut3.matches(T1.class.getMethod("foo"), T1.class)); System.out.println(pointcut3.matches(T1.class.getMethod("bar"), T1.class)); System.out.println(pointcut3.matches(T2.class.getMethod("foo"), T2.class)); System.out.println(pointcut3.matches(T3.class.getMethod("foo"), T3.class));
|
@Aspect与Advisor
先准备一个高级切面类Aspect1
和一个低级切面类Config
,并把两个切面类注册进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
| >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); } >}
>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())"); DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3); return advisor; } @Bean public MethodInterceptor advice3() { return new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("advice3 before"); Object res = invocation.proceed(); System.out.println("advice3 after"); return res; } }; } >}
|
AnnotationAwareAspectJAutoProxyCreator 作用
自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator
会帮我们创建代理
通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
高级的 @Aspect 切面会转换为低级的 Advisor 切面
findEligibleAdvisors方法
解析某个类的切面,一部分切面是低级的,如准备代码中的advisor3;另一部分是高级的, 由解析 @Aspect 后获得。解析时会将高级切面解析成多个低级切面
1 2
| AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class); List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
|
wrapIfNecessary方法
判断是否有必要对目标进行代理,内部调用 findEligibleAdvisors
, 只要返回集合不空, 则表示需要创建代理
如下面代码,因为在准备时Target2中的bar方法,没有与切点进行匹配,所以Target2在调用findEligibleAdvisors
方法后获取的切面集合为空,所以没必要进行代理
1 2 3 4
| 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());
|
代理创建时机
Bean创建的三个重要阶段分别为:创建 -> (?) 依赖注入 -> 初始化 (?)
代理创建的时机有两个位置,一个是创建Bean和依赖注入之间,另一个是初始化之后
初始化之后
下面代码中因为Bean1中有foo方法,所以Bean1将会被代理,而且在Bean2中还注入了Bean1。
在这种单向的依赖关系下(Bean2依赖Bean1),代理在Bean1的初始化之后创建,在代理创建之后,才继续执行Bean2的构造方法,在Bean2中注入Bean1的代理,最后完成Bean2的初始化
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
| @Bean public Advisor advisor(MethodInterceptor advice) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())"); return new DefaultPointcutAdvisor(pointcut, advice); } @Bean public MethodInterceptor advice() { return (MethodInvocation invocation) -> { System.out.println("before..."); return invocation.proceed(); }; }
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()"); } }
|
创建Bean和依赖注入之间
下面代码中因为Bean1中有foo方法,所以Bean1将会被代理,而且在Bean1中注入了Bean2,在Bean2中注入了Bean1。在这种循环依赖的关系下,代理在Bean1的构造和Bean1的依赖注入之间创建的。
原因:因为在Bean1中需要注入Bean2,所以执行完Bean1的构造后,就执行Bean2的构造,因为Bean2中需要注入Bean1的代理,所以再创建Bean1的代理。
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
| @Bean public Advisor advisor(MethodInterceptor advice) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())"); return new DefaultPointcutAdvisor(pointcut, advice); }
@Bean public MethodInterceptor advice() { return (MethodInvocation invocation) -> { System.out.println("before..."); return invocation.proceed(); }; }
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()"); } }
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()"); } }
|
切面顺序控制
使用@Order
注解进行控制,默认是最低优先级,值越小优先级越高
注意:可以加在高级切面类上,但对于低级切面类,需要在DefaultPointcutAdvisor
类中setOrder
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
| @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; } @Bean public MethodInterceptor advice3() { return new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("advice3 before"); Object res = invocation.proceed(); System.out.println("advice3 after"); return res; } }; } }
|
高级切面转为低级切面(@Before为例)
类似的有
- AspectJAroundAdvice (环绕通知)
- AspectJAfterReturningAdvice
- AspectJAfterThrowingAdvice (环绕通知)
- AspectJAfterAdvice (环绕通知)
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
| static class Aspect { @Before("execution(* foo())") public void before1() { System.out.println("before1"); } @Before("execution(* foo())") public void before2() { System.out.println("before2"); } }
static class Target { public void foo() { System.out.println("target foo"); } }
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); } }
|
静态通知调用
不同的通知统一转换成环绕通知
ProxyFactory 在创建代理时,最后调用 advice 的是一个 MethodInvocation 对象(调用链对象)
1、因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象
2、MethodInvocation调用次序如下
3、为了实现上图所示的调用次序,环绕通知最适合,因此其他 before、afterReturning 都会被转换成环绕通知
4、统一转换为环绕通知, 体现的是设计模式中的适配器模式(点击查看设计模式)
统一转换为 MethodInterceptor 环绕通知, 这体现在方法中的 Interceptors 上
- MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
- AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
- 对外是为了方便使用要区分 before、afterReturning
- 对内统一都是环绕通知, 统一用 MethodInterceptor 表示
定义了一个高级切面,里面含有多个通知类型
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
| 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 56
| @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.addAdvice(ExposeInvocationInterceptor.INSTANCE); proxyFactory.addAdvisors(list);
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class); for (Object o : methodInterceptorList) { System.out.println(o); } }
|
调用链执行过程
注意:在调用链执行的过程中,某些通知内部有可能会使用到调用链对象。所以必须使用一个最外层的环绕通知,将 MethodInvocation 放入当前线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList );
methodInvocation.proceed();
|
模拟实现调用链
在proceed方法内部首先需要将可调用次数count(初始化为1)与通知的list集合比较,如果可调用次数大,说明集合为空,没有通知,则直接调用目标。如果集合不为空,那么就递归调用下一个通知
注意:此处递归调用并不是直接在proceed方法内部调用proceed而是通过interceptor调用invoke,从而间接地在切面中调用proceed方法
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
| static class Target { public void foo() {System.out.println("Target.foo()");} }
static class Advice1 implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("Advice1 before..."); Object res = invocation.proceed(); System.out.println("Advice1 after..."); return res; } }
static class Advice2 implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("Advice2.before..."); Object result = invocation.proceed(); System.out.println("Advice2.after..."); return result; } }
static class MyInvocation implements MethodInvocation { private Object target; private Method method; private Object[] args; 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 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 Method getMethod() {return method;}
@Override public Object[] getArguments() {return args;} @Override public Object getThis() {return target;} @Override public AccessibleObject getStaticPart() {return method;} }
|
测试
1 2 3 4 5 6 7 8 9
| public static void main(String[] args) throws Throwable { Target target = new Target(); List<MethodInterceptor> list = List.of( new Advice1(), new Advice2() ); MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list); invocation.proceed(); }
|
动态通知调用