福州网站设计哪里建站,网站建设有证书吗,安徽安庆网站建设公司,肥西县市建设局网站文章目录 AOP9) AOP 实现之 ajc 编译器收获#x1f4a1; 10) AOP 实现之 agent 类加载收获#x1f4a1; 11) AOP 实现之 proxy演示1 - jdk 动态代理收获#x1f4a1;演示2 - cglib 代理收获#x1f4a1; 12) jdk 动态代理进阶演示1 - 模拟 jdk 动态代理收获#x1f4a1;演… 文章目录 AOP9) AOP 实现之 ajc 编译器收获 10) AOP 实现之 agent 类加载收获 11) AOP 实现之 proxy演示1 - jdk 动态代理收获演示2 - cglib 代理收获 12) jdk 动态代理进阶演示1 - 模拟 jdk 动态代理收获演示2 - 方法反射优化代码参考 收获 13) cglib 代理进阶演示 - 模拟 cglib 代理代码参考 收获 14) cglib 避免反射调用演示 - cglib 如何避免反射代码参考 收获 15) jdk 和 cglib 在 Spring 中的统一演示 - 底层切点、通知、切面代码参考 收获 16) 切点匹配演示 - 切点匹配代码参考 收获 17) 从 Aspect 到 Advisor演示1 - 代理创建器代码参考 收获演示2 - 代理创建时机代码参考 收获演示3 - Before 对应的低级通知代码参考 收获 18) 静态通知调用演示1 - 通知调用过程代码参考 收获演示2 - 模拟 MethodInvocation代码参考 收获 19) 动态通知调用演示 - 带参数绑定的通知方法调用代码参考 收获 AOP
AOP 底层实现方式之一是代理由代理结合通知和目标提供增强功能
除此以外aspectj 提供了两种另外的 AOP 底层实现 第一种是通过 ajc 编译器在编译 class 类文件时就把通知的增强功能织入到目标类的字节码中 第二种是通过 agent 在加载目标类时修改目标类的字节码织入增强功能 作为对比之前学习的代理是运行时生成新的字节码
简单比较的话
aspectj 在编译和加载时修改目标字节码性能较高aspectj 因为不用代理能突破一些技术上的限制例如对构造、对静态方法、对 final 也能增强但 aspectj 侵入性较强且需要学习新的 aspectj 特有语法因此没有广泛流行
9) AOP 实现之 ajc 编译器
代码参考
package com.itheima;import com.itheima.service.MyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*注意几点1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 162. 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器*/
SpringBootApplication
public class A09 {private static final Logger log LoggerFactory.getLogger(A09.class);public static void main(String[] args) {
// ConfigurableApplicationContext context SpringApplication.run(A10Application.class, args);
// MyService service context.getBean(MyService.class);
//
// log.debug(service class: {}, service.getClass());
// service.foo();
//
// context.close();new MyService().foo();/*学到了什么1. aop 的原理并非代理一种, 编译器也能玩出花样*/}
}
package com.itheima.aop;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;Aspect // ⬅️注意此切面并未被 Spring 管理
public class MyAspect {private static final Logger log LoggerFactory.getLogger(MyAspect.class);Before(execution(* com.itheima.service.MyService.foo()))public void before() {log.debug(before());}
}
package com.itheima.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;Service
public class MyService {private static final Logger log LoggerFactory.getLogger(MyService.class);public static void foo() {log.debug(foo());}
} dependencies..........dependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactId/dependencydependencygroupIdorg.aspectj/groupIdartifactIdaspectjrt/artifactId/dependency/dependencies
buildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/pluginplugingroupIdorg.codehaus.mojo/groupIdartifactIdaspectj-maven-plugin/artifactIdversion1.14.0/versionconfigurationcomplianceLevel1.8/complianceLevelsource8/sourcetarget8/targetshowWeaveInfotrue/showWeaveInfoverbosetrue/verboseXlintignore/XlintencodingUTF-8/encoding/configurationexecutionsexecutiongoals!-- use this goal to weave all your main classes --goalcompile/goal!-- use this goal to weave all your test classes --goaltest-compile/goal/goals/execution/executions/plugin/plugins/build收获
编译器也能修改 class 实现增强编译器增强能突破代理仅能通过方法重写增强的限制可以对构造方法、静态方法等实现增强 注意 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器 10) AOP 实现之 agent 类加载
代码参考
package com.itheima;import com.itheima.service.MyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;/*注意几点1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 162. 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址*/
SpringBootApplication
public class A10 {private static final Logger log LoggerFactory.getLogger(A10.class);public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(A10.class, args);MyService service context.getBean(MyService.class);// ⬇️MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码log.debug(service class: {}, service.getClass());service.foo();// context.close();/*学到了什么1. aop 的原理并非代理一种, agent 也能, 只要字节码变了, 行为就变了*/}
}
收获
类加载时可以通过 agent 修改 class 实现增强
11) AOP 实现之 proxy
演示1 - jdk 动态代理
public class JdkProxyDemo {interface Foo {void foo();}static class Target implements Foo {public void foo() {System.out.println(target foo);}}public static void main(String[] param) {// 目标对象Target target new Target();// 代理对象Foo proxy (Foo) Proxy.newProxyInstance(Target.class.getClassLoader(), new Class[]{Foo.class},(p, method, args) - {System.out.println(proxy before...);Object result method.invoke(target, args);System.out.println(proxy after...);return result;});// 调用代理proxy.foo();}
}运行结果
proxy before...
target foo
proxy after...收获
jdk 动态代理要求目标必须实现接口生成的代理类实现相同接口因此代理与目标之间是平级兄弟关系
演示2 - cglib 代理
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, (MethodInterceptor) (p, method, args, methodProxy) - {System.out.println(proxy before...);Object result methodProxy.invoke(target, args);// 另一种调用方法不需要目标对象实例
// Object result methodProxy.invokeSuper(p, args);System.out.println(proxy after...);return result;});// 调用代理proxy.foo();}
}运行结果与 jdk 动态代理相同
收获
cglib 不要求目标实现接口它生成的代理类是目标的子类因此代理与目标之间是子父关系限制⛔根据上述分析 final 类无法被 cglib 增强
12) jdk 动态代理进阶
演示1 - 模拟 jdk 动态代理
public class A12 {interface Foo {void foo();int bar();}static class Target implements Foo {public void foo() {System.out.println(target foo);}public int bar() {System.out.println(target bar);return 100;}}public static void main(String[] param) {// ⬇️1. 创建代理这时传入 InvocationHandlerFoo proxy new $Proxy0(new InvocationHandler() { // ⬇️5. 进入 InvocationHandlerpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{// ⬇️6. 功能增强System.out.println(before...);// ⬇️7. 反射调用目标方法return method.invoke(new Target(), args);}});// ⬇️2. 调用代理方法proxy.foo();proxy.bar();}
}模拟代理实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;// ⬇️这就是 jdk 代理类的源码, 秘密都在里面
public class $Proxy0 extends Proxy implements A12.Foo {public $Proxy0(InvocationHandler h) {super(h);}// ⬇️3. 进入代理方法public void foo() {try {// ⬇️4. 回调 InvocationHandlerh.invoke(this, foo, new Object[0]);} catch (RuntimeException | Error e) {throw e;} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}Overridepublic int bar() {try {Object result h.invoke(this, bar, new Object[0]);return (int) result;} catch (RuntimeException | Error e) {throw e;} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}static Method foo;static Method bar;static {try {foo A12.Foo.class.getMethod(foo);bar A12.Foo.class.getMethod(bar);} catch (NoSuchMethodException e) {throw new NoSuchMethodError(e.getMessage());}}
}收获
代理一点都不难无非就是利用了多态、反射的知识
方法重写可以增强逻辑只不过这【增强逻辑】千变万化不能写死在代理内部通过接口回调将【增强逻辑】置于代理类之外配合接口方法反射是多态调用就可以再联动调用目标方法会用 arthas 的 jad 工具反编译代理类限制⛔代理增强是借助多态来实现因此成员变量、静态方法、final 方法均不能通过代理实现
演示2 - 方法反射优化
代码参考
package com.itheima.a12;import java.lang.reflect.Field;
import java.lang.reflect.Method;// 运行时请添加 --add-opens java.base/java.lang.reflectALL-UNNAMED --add-opens java.base/jdk.internal.reflectALL-UNNAMED
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();}// 方法反射调用时, 底层 MethodAccessor 的实现类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);}
}
收获
前 16 次反射性能较低第 17 次调用会生成代理类优化为非反射调用会用 arthas 的 jad 工具反编译第 17 次调用生成的代理类 注意 运行时请添加 --add-opens java.base/java.lang.reflectALL-UNNAMED --add-opens java.base/jdk.internal.reflectALL-UNNAMED 13) cglib 代理进阶
演示 - 模拟 cglib 代理
代码参考
package com.itheima.a13;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class A13 {public static void main(String[] args) {Proxy proxy new Proxy();Target target new Target();proxy.setMethodInterceptor(new MethodInterceptor() {Overridepublic Object intercept(Object p, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {System.out.println(before...);
// return method.invoke(target, args); // 反射调用// FastClass
// return methodProxy.invoke(target, args); // 内部无反射, 结合目标用return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用}});proxy.save();proxy.save(1);proxy.save(2L);}
}
package com.itheima.a13;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;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 j) {super.save(j);}// 带增强功能的方法Overridepublic void save() {try {methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}Overridepublic void save(int i) {try {methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}Overridepublic void save(long j) {try {methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}
}
package com.itheima.a13;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));}
}
收获
和 jdk 动态代理原理查不多
回调的接口换了一下InvocationHandler 改成了 MethodInterceptor调用目标时有所改进见下面代码片段 method.invoke 是反射调用必须调用到足够次数才会进行优化methodProxy.invoke 是不反射调用它会正常间接调用目标对象的方法Spring 采用methodProxy.invokeSuper 也是不反射调用它会正常间接调用代理对象的方法可以省略目标对象
public class A14Application {public static void main(String[] args) throws InvocationTargetException {Target target new Target();Proxy proxy new Proxy();proxy.setCallbacks(new Callback[]{(MethodInterceptor) (p, m, a, mp) - {System.out.println(proxy before... mp.getSignature());// ⬇️调用目标方法(三种)
// Object result m.invoke(target, a); // ⬅️反射调用
// Object result mp.invoke(target, a); // ⬅️非反射调用, 结合目标用Object result mp.invokeSuper(p, a); // ⬅️非反射调用, 结合代理用System.out.println(proxy after... mp.getSignature());return result;}});// ⬇️调用代理方法proxy.save();}
}注意 调用 Object 的方法, 后两种在 jdk 9 时都有问题, 需要 --add-opens java.base/java.langALL-UNNAMED 14) cglib 避免反射调用
演示 - cglib 如何避免反射
代码参考
package com.itheima.a13;import org.springframework.cglib.core.Signature;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);// 获取目标方法的编号/*Targetsave() 0save(int) 1save(long) 2signature 包括方法名字、参数返回值*/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;}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(无此方法);}}public static void main(String[] args) {TargetFastClass fastClass new TargetFastClass();int index fastClass.getIndex(new Signature(save, (I)V));System.out.println(index);fastClass.invoke(index, new Target(), new Object[]{100});}
}
package com.itheima.a13;import org.springframework.cglib.core.Signature;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);// 获取代理方法的编号/*ProxysaveSuper() 0saveSuper(int) 1saveSuper(long) 2signature 包括方法名字、参数返回值*/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;}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(index, new Proxy(), new Object[0]);}
}
收获
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类 ProxyFastClass 配合代理对象一起使用, 避免反射TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种) TargetFastClass 记录了 Target 中方法与编号的对应关系 save(long) 编号 2save(int) 编号 1save() 编号 0首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射 ProxyFastClass 记录了 Proxy 中方法与编号的对应关系不过 Proxy 额外提供了下面几个方法 saveSuper(long) 编号 2不增强仅是调用 super.save(long)saveSuper(int) 编号 1不增强, 仅是调用 super.save(int)saveSuper() 编号 0不增强, 仅是调用 super.save()查找方式与 TargetFastClass 类似 为什么有这么麻烦的一套东西呢 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
15) jdk 和 cglib 在 Spring 中的统一
Spring 中对切点、通知、切面的抽象如下
切点接口 Pointcut典型实现 AspectJExpressionPointcut通知典型接口为 MethodInterceptor 代表环绕通知切面Advisor包含一个 Advice 通知PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut #mermaid-svg-Oc1AIKMZkrcau4I5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Oc1AIKMZkrcau4I5 .error-icon{fill:#552222;}#mermaid-svg-Oc1AIKMZkrcau4I5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Oc1AIKMZkrcau4I5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Oc1AIKMZkrcau4I5 .marker.cross{stroke:#333333;}#mermaid-svg-Oc1AIKMZkrcau4I5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Oc1AIKMZkrcau4I5 g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-Oc1AIKMZkrcau4I5 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-Oc1AIKMZkrcau4I5 .nodeLabel,#mermaid-svg-Oc1AIKMZkrcau4I5 .edgeLabel{color:#131300;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-Oc1AIKMZkrcau4I5 .label text{fill:#131300;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-Oc1AIKMZkrcau4I5 .classTitle{font-weight:bolder;}#mermaid-svg-Oc1AIKMZkrcau4I5 .node rect,#mermaid-svg-Oc1AIKMZkrcau4I5 .node circle,#mermaid-svg-Oc1AIKMZkrcau4I5 .node ellipse,#mermaid-svg-Oc1AIKMZkrcau4I5 .node polygon,#mermaid-svg-Oc1AIKMZkrcau4I5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Oc1AIKMZkrcau4I5 .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 g.clickable{cursor:pointer;}#mermaid-svg-Oc1AIKMZkrcau4I5 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Oc1AIKMZkrcau4I5 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Oc1AIKMZkrcau4I5 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-Oc1AIKMZkrcau4I5 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Oc1AIKMZkrcau4I5 .dashed-line{stroke-dasharray:3;}#mermaid-svg-Oc1AIKMZkrcau4I5 #compositionStart,#mermaid-svg-Oc1AIKMZkrcau4I5 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #compositionEnd,#mermaid-svg-Oc1AIKMZkrcau4I5 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #dependencyStart,#mermaid-svg-Oc1AIKMZkrcau4I5 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #dependencyStart,#mermaid-svg-Oc1AIKMZkrcau4I5 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #extensionStart,#mermaid-svg-Oc1AIKMZkrcau4I5 .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #extensionEnd,#mermaid-svg-Oc1AIKMZkrcau4I5 .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #aggregationStart,#mermaid-svg-Oc1AIKMZkrcau4I5 .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 #aggregationEnd,#mermaid-svg-Oc1AIKMZkrcau4I5 .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Oc1AIKMZkrcau4I5 .edgeTerminals{font-size:11px;}#mermaid-svg-Oc1AIKMZkrcau4I5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 一 一 «interface» Advice «interface» MethodInterceptor «interface» Advisor «interface» PointcutAdvisor «interface» Pointcut AspectJExpressionPointcut 代理相关类图
AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现AopProxy 通过 getProxy 创建代理对象图中 Proxy 都实现了 Advised 接口能够获得关联的切面集合与目标其实是从 ProxyFactory 取得调用代理方法时会借助 ProxyFactory 将通知统一转为环绕通知MethodInterceptor #mermaid-svg-ybj0TU6ONo0UiBCd {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ybj0TU6ONo0UiBCd .error-icon{fill:#552222;}#mermaid-svg-ybj0TU6ONo0UiBCd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ybj0TU6ONo0UiBCd .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-ybj0TU6ONo0UiBCd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ybj0TU6ONo0UiBCd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ybj0TU6ONo0UiBCd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ybj0TU6ONo0UiBCd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ybj0TU6ONo0UiBCd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ybj0TU6ONo0UiBCd .marker.cross{stroke:#333333;}#mermaid-svg-ybj0TU6ONo0UiBCd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ybj0TU6ONo0UiBCd g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-ybj0TU6ONo0UiBCd g.classGroup text .title{font-weight:bolder;}#mermaid-svg-ybj0TU6ONo0UiBCd .nodeLabel,#mermaid-svg-ybj0TU6ONo0UiBCd .edgeLabel{color:#131300;}#mermaid-svg-ybj0TU6ONo0UiBCd .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-ybj0TU6ONo0UiBCd .label text{fill:#131300;}#mermaid-svg-ybj0TU6ONo0UiBCd .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-ybj0TU6ONo0UiBCd .classTitle{font-weight:bolder;}#mermaid-svg-ybj0TU6ONo0UiBCd .node rect,#mermaid-svg-ybj0TU6ONo0UiBCd .node circle,#mermaid-svg-ybj0TU6ONo0UiBCd .node ellipse,#mermaid-svg-ybj0TU6ONo0UiBCd .node polygon,#mermaid-svg-ybj0TU6ONo0UiBCd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ybj0TU6ONo0UiBCd .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-ybj0TU6ONo0UiBCd g.clickable{cursor:pointer;}#mermaid-svg-ybj0TU6ONo0UiBCd g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-ybj0TU6ONo0UiBCd g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-ybj0TU6ONo0UiBCd .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-ybj0TU6ONo0UiBCd .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-ybj0TU6ONo0UiBCd .dashed-line{stroke-dasharray:3;}#mermaid-svg-ybj0TU6ONo0UiBCd #compositionStart,#mermaid-svg-ybj0TU6ONo0UiBCd .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #compositionEnd,#mermaid-svg-ybj0TU6ONo0UiBCd .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #dependencyStart,#mermaid-svg-ybj0TU6ONo0UiBCd .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #dependencyStart,#mermaid-svg-ybj0TU6ONo0UiBCd .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #extensionStart,#mermaid-svg-ybj0TU6ONo0UiBCd .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #extensionEnd,#mermaid-svg-ybj0TU6ONo0UiBCd .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #aggregationStart,#mermaid-svg-ybj0TU6ONo0UiBCd .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd #aggregationEnd,#mermaid-svg-ybj0TU6ONo0UiBCd .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ybj0TU6ONo0UiBCd .edgeTerminals{font-size:11px;}#mermaid-svg-ybj0TU6ONo0UiBCd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 多 使用 创建 创建 «interface» Advised ProxyFactory proxyTargetClass : boolean Target Advisor «interface» AopProxyFactory «interface» AopProxy getProxy() : Object 基于CGLIB的Proxy ObjenesisCglibAopProxy advised : ProxyFactory JdkDynamicAopProxy advised : ProxyFactory 基于JDK的Proxy 演示 - 底层切点、通知、切面
代码参考
package com.itheima.a15;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;public class A15 {public static void main(String[] args) {/*两个切面概念aspect 通知1(advice) 切点1(pointcut)通知2(advice) 切点2(pointcut)通知3(advice) 切点3(pointcut)...advisor 更细粒度的切面包含一个通知和切点*/// 1. 备好切点AspectJExpressionPointcut pointcut new AspectJExpressionPointcut();pointcut.setExpression(execution(* foo()));// 2. 备好通知MethodInterceptor advice invocation - {System.out.println(before...);Object result invocation.proceed(); // 调用目标System.out.println(after...);return result;};// 3. 备好切面DefaultPointcutAdvisor advisor new DefaultPointcutAdvisor(pointcut, advice);/*4. 创建代理a. proxyTargetClass false, 目标实现了接口, 用 jdk 实现b. proxyTargetClass false, 目标没有实现接口, 用 cglib 实现c. proxyTargetClass true, 总是使用 cglib 实现*/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();/*学到了什么a. Spring 的代理选择规则b. 底层的切点实现c. 底层的通知实现d. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现- JdkDynamicAopProxy- ObjenesisCglibAopProxy*/}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);}}
}
收获
底层的切点实现底层的通知实现底层的切面实现ProxyFactory 用来创建代理 如果指定了接口且 proxyTargetClass false使用 JdkDynamicAopProxy如果没有指定接口或者 proxyTargetClass true使用 ObjenesisCglibAopProxy 例外如果目标是接口类型或已经是 Jdk 代理使用 JdkDynamicAopProxy 注意 要区分本章节提到的 MethodInterceptor它与之前 cglib 中用的的 MethodInterceptor 是不同的接口 16) 切点匹配
演示 - 切点匹配
代码参考
package com.itheima.a16;import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;import java.lang.reflect.Method;public class A16 {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));StaticMethodMatcherPointcut pt3 new StaticMethodMatcherPointcut() {Overridepublic boolean matches(Method method, Class? targetClass) {// 检查方法上是否加了 Transactional 注解MergedAnnotations annotations MergedAnnotations.from(method);if (annotations.isPresent(Transactional.class)) {return true;}// 查看类上是否加了 Transactional 注解annotations MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);if (annotations.isPresent(Transactional.class)) {return true;}return false;}};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));/*学到了什么a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法的匹配*/}static class T1 {Transactionalpublic void foo() {}public void bar() {}}Transactionalstatic class T2 {public void foo() {}}Transactionalinterface I3 {void foo();}static class T3 implements I3 {public void foo() {}}
}
收获
常见 aspectj 切点用法aspectj 切点的局限性实际的 Transactional 切点实现
17) 从 Aspect 到 Advisor
演示1 - 代理创建器
代码参考
package org.springframework.aop.framework.autoproxy;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;import java.util.List;public class A17 {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);// BeanPostProcessor// 创建 - (*) 依赖注入 - 初始化 (*)context.refresh();
// for (String name : context.getBeanDefinitionNames()) {
// System.out.println(name);
// }/*第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisorsa. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 Aspect 后获得*/AnnotationAwareAspectJAutoProxyCreator creator context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);ListAdvisor advisors creator.findEligibleAdvisors(Target2.class, target2);/*for (Advisor advisor : advisors) {System.out.println(advisor);}*//*第二个重要方法 wrapIfNecessarya. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理*/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());((Target1) o1).foo();/*学到了什么a. 自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator 会帮我们创建代理b. 通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行c. 高级的 Aspect 切面会转换为低级的 Advisor 切面, 理解原理, 大道至简*/}static class Target1 {public void foo() {System.out.println(target1 foo);}}static class Target2 {public void bar() {System.out.println(target2 bar);}}Aspect // 高级切面类Order(1)static class Aspect1 {Before(execution(* foo()))public void before1() {System.out.println(aspect1 before1...);}Before(execution(* foo()))public void before2() {System.out.println(aspect1 before2...);}}Configurationstatic class Config {/*Bean // 低级切面public Advisor advisor3(MethodInterceptor advice3) {AspectJExpressionPointcut pointcut new AspectJExpressionPointcut();pointcut.setExpression(execution(* foo()));DefaultPointcutAdvisor advisor new DefaultPointcutAdvisor(pointcut, advice3);return advisor;}Beanpublic MethodInterceptor advice3() {return invocation - {System.out.println(advice3 before...);Object result invocation.proceed();System.out.println(advice3 after...);return result;};}*/}}
收获
AnnotationAwareAspectJAutoProxyCreator 的作用 将高级 Aspect 切面统一为低级 Advisor 切面在合适的时机创建代理 findEligibleAdvisors 找到有【资格】的 Advisors 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3有【资格】的 Advisor 另一部分是高级的, 由解析 Aspect 后获得 wrapIfNecessary 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
演示2 - 代理创建时机
代码参考
package org.springframework.aop.framework.autoproxy;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;import javax.annotation.PostConstruct;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();// 创建 - (*) 依赖注入 - 初始化 (*)/*学到了什么a. 代理的创建时机1. 初始化之后 (无循环依赖时)2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象*/}Configurationstatic class Config {Bean // 解析 Aspect、产生代理public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {return new AnnotationAwareAspectJAutoProxyCreator();}Bean // 解析 Autowiredpublic AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {return new AutowiredAnnotationBeanPostProcessor();}Bean // 解析 PostConstructpublic CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {return new CommonAnnotationBeanPostProcessor();}Beanpublic Advisor advisor(MethodInterceptor advice) {AspectJExpressionPointcut pointcut new AspectJExpressionPointcut();pointcut.setExpression(execution(* foo()));return new DefaultPointcutAdvisor(pointcut, advice);}Beanpublic MethodInterceptor advice() {return (MethodInvocation invocation) - {System.out.println(before...);return invocation.proceed();};}Beanpublic Bean1 bean1() {return new Bean1();}Beanpublic Bean2 bean2() {return new Bean2();}}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());}}
}
收获
代理的创建时机 初始化之后 (无循环依赖时)实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
演示3 - Before 对应的低级通知
代码参考
package org.springframework.aop.framework.autoproxy;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class A17_2 {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);}}SuppressWarnings(all)public static void main(String[] args) throws Throwable {AspectInstanceFactory factory new SingletonAspectInstanceFactory(new Aspect());// 高级切面转低级切面类ListAdvisor 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);}/*Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息a. 通知代码从哪儿来b. 切点是什么(这里为啥要切点, 后面解释)c. 通知对象如何创建, 本例共用同一个 Aspect 对象类似的通知还有1. AspectJAroundAdvice (环绕通知)2. AspectJAfterReturningAdvice3. AspectJAfterThrowingAdvice4. AspectJAfterAdvice (环绕通知)*/}
}
收获
Before 前置通知会被转换为原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息 通知代码从哪儿来切点是什么(这里为啥要切点, 后面解释)通知对象如何创建, 本例共用同一个 Aspect 对象 类似的还有 AspectJAroundAdvice (环绕通知)AspectJAfterReturningAdviceAspectJAfterThrowingAdvice (环绕通知)AspectJAfterAdvice (环绕通知)
18) 静态通知调用
代理对象调用流程如下以 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返回最终的结果
图中不同颜色对应一次环绕通知或目标的调用起始至终结 #mermaid-svg-GiIrjCBQSdK2z8Nv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv .error-icon{fill:#552222;}#mermaid-svg-GiIrjCBQSdK2z8Nv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GiIrjCBQSdK2z8Nv .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-GiIrjCBQSdK2z8Nv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GiIrjCBQSdK2z8Nv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GiIrjCBQSdK2z8Nv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GiIrjCBQSdK2z8Nv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GiIrjCBQSdK2z8Nv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GiIrjCBQSdK2z8Nv .marker.cross{stroke:#333333;}#mermaid-svg-GiIrjCBQSdK2z8Nv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GiIrjCBQSdK2z8Nv .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-GiIrjCBQSdK2z8Nv text.actortspan{fill:black;stroke:none;}#mermaid-svg-GiIrjCBQSdK2z8Nv .actor-line{stroke:grey;}#mermaid-svg-GiIrjCBQSdK2z8Nv .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv .sequenceNumber{fill:white;}#mermaid-svg-GiIrjCBQSdK2z8Nv #sequencenumber{fill:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv .messageText{fill:#333;stroke:#333;}#mermaid-svg-GiIrjCBQSdK2z8Nv .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-GiIrjCBQSdK2z8Nv .labelText,#mermaid-svg-GiIrjCBQSdK2z8Nv .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-GiIrjCBQSdK2z8Nv .loopText,#mermaid-svg-GiIrjCBQSdK2z8Nv .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-GiIrjCBQSdK2z8Nv .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-GiIrjCBQSdK2z8Nv .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-GiIrjCBQSdK2z8Nv .noteText,#mermaid-svg-GiIrjCBQSdK2z8Nv .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-GiIrjCBQSdK2z8Nv .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-GiIrjCBQSdK2z8Nv .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-GiIrjCBQSdK2z8Nv .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-GiIrjCBQSdK2z8Nv .actorPopupMenu{position:absolute;}#mermaid-svg-GiIrjCBQSdK2z8Nv .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-GiIrjCBQSdK2z8Nv .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-GiIrjCBQSdK2z8Nv .actor-man circle,#mermaid-svg-GiIrjCBQSdK2z8Nv line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-GiIrjCBQSdK2z8Nv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Proxy InvocationHandler MethodInvocation ProxyFactory MethodInterceptor1 MethodInterceptor2 Target invoke() 获得 Target 获得 MethodInterceptor 链 创建 mi mi.proceed() invoke(mi) 前增强 mi.proceed() invoke(mi) 前增强 mi.proceed() mi.invokeJoinPoint() 结果 后增强 结果 后增强 结果 Proxy InvocationHandler MethodInvocation ProxyFactory MethodInterceptor1 MethodInterceptor2 Target 演示1 - 通知调用过程
代码参考
package org.springframework.aop.framework;import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class A18 {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);}}SuppressWarnings(all)public static void main(String[] args) throws Throwable {AspectInstanceFactory factory new SingletonAspectInstanceFactory(new Aspect());// 1. 高级切面转低级切面类ListAdvisor 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);}/*Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息a. 通知代码从哪儿来b. 切点是什么c. 通知对象如何创建, 本例共用同一个 Aspect 对象类似的通知还有1. AspectJAroundAdvice (环绕通知)2. AspectJAfterReturningAdvice3. AspectJAfterThrowingAdvice (环绕通知)4. AspectJAfterAdvice (环绕通知)*/// 2. 通知统一转换为环绕通知 MethodInterceptor/*其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocationb. MethodInvocation 要知道 advice 有哪些, 还要知道目标, 调用次序如下将 MethodInvocation 放入当前线程|- before1 ----------------------------------- 从当前线程获取 MethodInvocation| || |- before2 -------------------- | 从当前线程获取 MethodInvocation| | | || | |- target ------ 目标 advice2 advice1| | | || |- after2 --------------------- || ||- after1 ------------------------------------c. 从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式- 对外是为了方便使用要区分 before、afterReturning- 对内统一都是环绕通知, 统一用 MethodInterceptor 表示此步获取所有执行时需要的 advice (静态)a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上b. 适配如下- MethodBeforeAdviceAdapter 将 Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor- AfterReturningAdviceAdapter 将 AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor*/Target target new Target();ProxyFactory proxyFactory new ProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程proxyFactory.addAdvisors(list);System.out.println();ListObject methodInterceptorList proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod(foo), Target.class);for (Object o : methodInterceptorList) {System.out.println(o);}System.out.println();// 3. 创建并执行调用链 (环绕通知s 目标)MethodInvocation methodInvocation new ReflectiveMethodInvocation(null, target, Target.class.getMethod(foo), new Object[0], Target.class, methodInterceptorList);methodInvocation.proceed();/*学到了什么a. 无参数绑定的通知如何被调用b. MethodInvocation 编程技巧: 拦截器、过滤器等等实现都与此类似c. 适配器模式在 Spring 中的体现*/}
}
收获
代理方法执行时会做如下工作
通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知 MethodBeforeAdviceAdapter 将 Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptorAfterReturningAdviceAdapter 将 AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor这体现的是适配器设计模式 所谓静态通知体现在上面方法的 Interceptors 部分这些通知调用时无需再次检查切点直接调用即可结合目标与环绕通知链创建 MethodInvocation 对象通过它完成整个调用
演示2 - 模拟 MethodInvocation
代码参考
package org.springframework.aop.framework;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;/*模拟调用链过程, 是一个简单的递归过程1. proceed() 方法调用链中下一个环绕通知2. 每个环绕通知内部继续调用 proceed()3. 调用到没有更多通知了, 就调用目标方法*/
public class A18_1 {static class Target {public void foo() {System.out.println(Target.foo());}}static class Advice1 implements MethodInterceptor {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 {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; // 1private Method method;private Object[] args;ListMethodInterceptor methodInterceptorList; // 2private int count 1; // 调用次数public MyInvocation(Object target, Method method, Object[] args, ListMethodInterceptor methodInterceptorList) {this.target target;this.method method;this.args args;this.methodInterceptorList methodInterceptorList;}Overridepublic Method getMethod() {return method;}Overridepublic Object[] getArguments() {return args;}Overridepublic Object proceed() throws Throwable { // 调用每一个环绕通知, 调用目标if (count methodInterceptorList.size()) {// 调用目标 返回并结束递归return method.invoke(target, args);}// 逐一调用通知, count 1MethodInterceptor methodInterceptor methodInterceptorList.get(count - 1);return methodInterceptor.invoke(this);}Overridepublic Object getThis() {return target;}Overridepublic AccessibleObject getStaticPart() {return method;}}public static void main(String[] args) throws Throwable {Target target new Target();ListMethodInterceptor list List.of(new Advice1(),new Advice2());MyInvocation invocation new MyInvocation(target, Target.class.getMethod(foo), new Object[0], list);invocation.proceed();}
}
收获
proceed() 方法调用链中下一个环绕通知每个环绕通知内部继续调用 proceed()调用到没有更多通知了, 就调用目标方法
MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上
19) 动态通知调用
演示 - 带参数绑定的通知方法调用
代码参考
package org.springframework.aop.framework.autoproxy;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;import java.lang.reflect.Field;
import java.util.List;public class A19 {Aspectstatic 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(before2(%d)%n, x);}}static class Target {public void foo(int x) {System.out.printf(target foo(%d)%n, x);}}Configurationstatic class MyConfig {BeanAnnotationAwareAspectJAutoProxyCreator proxyCreator() {return new AnnotationAwareAspectJAutoProxyCreator();}Beanpublic MyAspect myAspect() {return new MyAspect();}}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);ListAdvisor list creator.findEligibleAdvisors(Target.class, target);Target target new Target();ProxyFactory factory new ProxyFactory();factory.setTarget(target);factory.addAdvisors(list);Object proxy factory.getProxy(); // 获取代理ListObject interceptorList factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod(foo, int.class), Target.class);for (Object o : interceptorList) {showDetail(o);}System.out.println();ReflectiveMethodInvocation invocation new ReflectiveMethodInvocation(proxy, target, Target.class.getMethod(foo, int.class), new Object[]{100}, Target.class, interceptorList) {};invocation.proceed();/*学到了什么a. 有参数绑定的通知调用时还需要切点对参数进行匹配及绑定b. 复杂程度高, 性能比无参数绑定的通知调用低*/}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) {e.printStackTrace();}}
}
收获
通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知所谓动态通知体现在上面方法的 DynamicInterceptionAdvice 部分这些通知调用时因为要为通知方法绑定参数还需再次利用切点表达式动态通知调用复杂程度高性能较低