提高网站关键词排名,wordpress ctf,vancl官网,wordpress做成可选择语言aop实现方式 aspectj 编译器增强#xff0c;直接修改源码可以不借助Spring实现 也没有用代理对象 #xff08;ajc编译器#xff09; aop 的原理并非代理一种, 编译器也能玩出花样#xff08;直接修改源码#xff09; 运行时需要在 VM options 里加入 -javaagent:D:/envir…aop实现方式 aspectj 编译器增强直接修改源码可以不借助Spring实现 也没有用代理对象 ajc编译器 aop 的原理并非代理一种, 编译器也能玩出花样直接修改源码 运行时需要在 VM options 里加入 -javaagent:D:/environment/apache-maven-3.6.3/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar 把其中 D:/environment/apache-maven-3.6.3/repository 改为自己 maven 仓库起始地址 aop 的原理并非代理一种, agent 也能, 只要字节码变了, 行为就变了类加载阶段增强 代理 jdk or cglibspring中
jdk代理基于接口和实现类代理类与实现类平级
public class JdkProxyDemo {interface Foo {void foo();}static final class Target implements Foo {public void foo() {System.out.println(target foo);}}// jdk 【只能针对接口代理】 代理类和被代理类是兄弟关系平级的被代理类 是 final也无关紧要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}, (p, method, args) - {System.out.println(before...);// 目标.方法(参数)// 方法.invoke(目标, 参数); 反射调用Object result method.invoke(target, args);System.out.println(after....);return result; // 让代理也返回目标方法执行的结果});// 使用arthas工具将动态生成字节码文件反编译为class类文件System.out.println(proxy.getClass());proxy.foo();System.in.read();}
}cglib代理基于子父类关系父子关系
public class CglibProxyDemo {static class Target {public void foo() {System.out.println(target foo);}}// 代理是子类型目标是父类型 目标类加 final 就无法创建子类直接报错 【cglib 基于子父类关系代理】// 增强目标不能是 final 增强方法也不能是 finalpublic static void main(String[] param) {Target target new Target();Target proxy (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) - {System.out.println(before...);
// Object result method.invoke(target, args); // 用方法反射调用目标 方法一// methodProxy 它可以避免反射调用
// Object result methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 spring方法二Object result methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理 方法三System.out.println(after...);return result;});proxy.foo();}
}jdk代理原理【InvocationHandler】
接口实现类 interface Foo {void foo();int bar();}static class Target implements Foo {public void foo() {System.out.println(target foo);}Overridepublic int bar() {System.out.println(target bar);return 100;}}
asm技术生成字节码反编译生成类与手动实现代理类类似可以使用arthas工具将字节码反编译生成class文件【代理类】
运行程序打开arthas工具 看到运行程序输入3回车
输入如下命令将字节码反编译为Java类文件 /*** asm生成字节码反编译生成的Java代理类与手动实现类似*/
final class $Proxy0 extends Proxy implements JdkProxyDemo.Foo {private static final Method m0;private static final Method m1;private static final Method m2;private static final Method m3;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}Overridepublic final void foo() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}static {try {m0 Class.forName(java.lang.Object).getMethod(hashCode, new Class[0]);m1 Class.forName(java.lang.Object).getMethod(equals, Class.forName(java.lang.Object));m2 Class.forName(java.lang.Object).getMethod(toString, new Class[0]);m3 Class.forName(com.lkl.spring.chapter11.JdkProxyDemo$Foo).getMethod(foo, new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException {if (lookup.lookupClass() Proxy.class lookup.hasFullPrivilegeAccess()) {return MethodHandles.lookup();}throw new IllegalAccessException(lookup.toString());}
}/*** JDK动态代理实现相同接口生成代理类手动实现代理类*/
public class $Proxy0 extends Proxy implements JDKTest.Foo {public $Proxy0(InvocationHandler h) {super(h);}Overridepublic void foo() {try {// 不确定的方法使用接口抽象h.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 JDKTest.Foo.class.getMethod(foo);bar JDKTest.Foo.class.getMethod(bar);} catch (NoSuchMethodException e) {throw new NoSuchMethodError(e.getMessage());}}
}调用生成代理类实现增强 // 生成代理对象Foo proxy new $Proxy0(new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(before...);return method.invoke(new Target(), args);}});// java 动态代理 直接生成字节码 【asm技术】proxy.foo();System.out.println(proxy.bar());全过程 cglib代理原理【MethodInterceptor】
目标类
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));}
}代理类
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;/*cglib 一个代理类会生成两个代理对象jdk 大于16次一个就会产生一个代理对象*/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);}}
}和 jdk 动态代理原理查不多
回调的接口换了一下InvocationHandler 改成了 MethodInterceptor调用目标时有所改进见下面代码片段 method.invoke 是反射调用必须调用到足够次数才会进行优化methodProxy.invoke 是不反射调用它会正常间接调用目标对象的方法Spring 采用methodProxy.invokeSuper 也是不反射调用它会正常间接调用代理对象的方法可以省略目标对象
public class CglibTest {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 {return method.invoke(target, args); // 反射调用// FastClass实际上还是代理 MethodProxy
// return methodProxy.invoke(target, args); // 内部无反射结合目标用
// return methodProxy.invokeSuper(p, args); // 内部无反射结合代理用}});proxy.save();proxy.save(1);proxy.save(2L);}
}cglib 避免反射调用
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类父类都是FastClass ProxyFastClass 配合代理对象一起使用, 避免反射TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种) TargetFastClass 记录了 Target 中方法与编号的对应关系Signature save(long) 编号 2save(int) 编号 1save() 编号 0首先根据方法名和参数个数、类型, 用 switch 或 if 找到这些方法编号然后再根据编号去调用目标方法,又用了一大堆 switch 或 if但避免了反射 ProxyFastClass 记录了 Proxy 中方法与编号的对应关系不过 Proxy 额外提供了下面几个方法 saveSuper(long) 编号 5不增强仅是调用 super.save(long)saveSuper(int) 编号 4不增强, 仅是调用 super.save(int)saveSuper() 编号 3不增强, 仅是调用 super.save()查找方式与 TargetFastClass 类似 为什么有这么麻烦的一套东西呢 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
/*** 需要代理对象*/
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() 3saveSuper(int) 4saveSuper(long) 5signature 包括方法名字、参数返回值*/public int getIndex(Signature signature) {if (s0.equals(signature)) {return 3;} else if (s1.equals(signature)) {return 4;} else if (s2.equals(signature)) {return 5;}return -1;}// 根据方法编号, 正常调用目标对象方法public Object invoke(int index, Object proxy, Object[] args) {if (index 3) {((Proxy) proxy).saveSuper();return null;} else if (index 4) {((Proxy) proxy).saveSuper((int) args[0]);return null;} else if (index 5) {((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]);}
}/*** 需要目标对象*/
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});}
}配合代理类中 MethodProxy实现不反射调用方法
jdk 和 cglib 在 Spring 中的统一
Spring 中对切点、通知、切面的抽象如下
切点接口 Pointcut典型实现 AspectJExpressionPointcut通知接口Advice典型接口为 MethodInterceptor 代表环绕通知切面Advisor包含一个 Advice 通知PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut #mermaid-svg-yvEJTUjxnPPh3dcM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-yvEJTUjxnPPh3dcM .error-icon{fill:#552222;}#mermaid-svg-yvEJTUjxnPPh3dcM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yvEJTUjxnPPh3dcM .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-yvEJTUjxnPPh3dcM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yvEJTUjxnPPh3dcM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yvEJTUjxnPPh3dcM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yvEJTUjxnPPh3dcM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yvEJTUjxnPPh3dcM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yvEJTUjxnPPh3dcM .marker.cross{stroke:#333333;}#mermaid-svg-yvEJTUjxnPPh3dcM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yvEJTUjxnPPh3dcM g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-yvEJTUjxnPPh3dcM g.classGroup text .title{font-weight:bolder;}#mermaid-svg-yvEJTUjxnPPh3dcM .nodeLabel,#mermaid-svg-yvEJTUjxnPPh3dcM .edgeLabel{color:#131300;}#mermaid-svg-yvEJTUjxnPPh3dcM .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-yvEJTUjxnPPh3dcM .label text{fill:#131300;}#mermaid-svg-yvEJTUjxnPPh3dcM .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-yvEJTUjxnPPh3dcM .classTitle{font-weight:bolder;}#mermaid-svg-yvEJTUjxnPPh3dcM .node rect,#mermaid-svg-yvEJTUjxnPPh3dcM .node circle,#mermaid-svg-yvEJTUjxnPPh3dcM .node ellipse,#mermaid-svg-yvEJTUjxnPPh3dcM .node polygon,#mermaid-svg-yvEJTUjxnPPh3dcM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yvEJTUjxnPPh3dcM .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-yvEJTUjxnPPh3dcM g.clickable{cursor:pointer;}#mermaid-svg-yvEJTUjxnPPh3dcM g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-yvEJTUjxnPPh3dcM g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-yvEJTUjxnPPh3dcM .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-yvEJTUjxnPPh3dcM .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-yvEJTUjxnPPh3dcM .dashed-line{stroke-dasharray:3;}#mermaid-svg-yvEJTUjxnPPh3dcM #compositionStart,#mermaid-svg-yvEJTUjxnPPh3dcM .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #compositionEnd,#mermaid-svg-yvEJTUjxnPPh3dcM .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #dependencyStart,#mermaid-svg-yvEJTUjxnPPh3dcM .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #dependencyStart,#mermaid-svg-yvEJTUjxnPPh3dcM .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #extensionStart,#mermaid-svg-yvEJTUjxnPPh3dcM .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #extensionEnd,#mermaid-svg-yvEJTUjxnPPh3dcM .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #aggregationStart,#mermaid-svg-yvEJTUjxnPPh3dcM .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM #aggregationEnd,#mermaid-svg-yvEJTUjxnPPh3dcM .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-yvEJTUjxnPPh3dcM .edgeTerminals{font-size:11px;}#mermaid-svg-yvEJTUjxnPPh3dcM :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-xkiDCjxKjC2DSg1m {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xkiDCjxKjC2DSg1m .error-icon{fill:#552222;}#mermaid-svg-xkiDCjxKjC2DSg1m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xkiDCjxKjC2DSg1m .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-xkiDCjxKjC2DSg1m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xkiDCjxKjC2DSg1m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xkiDCjxKjC2DSg1m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xkiDCjxKjC2DSg1m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xkiDCjxKjC2DSg1m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xkiDCjxKjC2DSg1m .marker.cross{stroke:#333333;}#mermaid-svg-xkiDCjxKjC2DSg1m svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xkiDCjxKjC2DSg1m g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-xkiDCjxKjC2DSg1m g.classGroup text .title{font-weight:bolder;}#mermaid-svg-xkiDCjxKjC2DSg1m .nodeLabel,#mermaid-svg-xkiDCjxKjC2DSg1m .edgeLabel{color:#131300;}#mermaid-svg-xkiDCjxKjC2DSg1m .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-xkiDCjxKjC2DSg1m .label text{fill:#131300;}#mermaid-svg-xkiDCjxKjC2DSg1m .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-xkiDCjxKjC2DSg1m .classTitle{font-weight:bolder;}#mermaid-svg-xkiDCjxKjC2DSg1m .node rect,#mermaid-svg-xkiDCjxKjC2DSg1m .node circle,#mermaid-svg-xkiDCjxKjC2DSg1m .node ellipse,#mermaid-svg-xkiDCjxKjC2DSg1m .node polygon,#mermaid-svg-xkiDCjxKjC2DSg1m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xkiDCjxKjC2DSg1m .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-xkiDCjxKjC2DSg1m g.clickable{cursor:pointer;}#mermaid-svg-xkiDCjxKjC2DSg1m g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-xkiDCjxKjC2DSg1m g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-xkiDCjxKjC2DSg1m .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-xkiDCjxKjC2DSg1m .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-xkiDCjxKjC2DSg1m .dashed-line{stroke-dasharray:3;}#mermaid-svg-xkiDCjxKjC2DSg1m #compositionStart,#mermaid-svg-xkiDCjxKjC2DSg1m .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #compositionEnd,#mermaid-svg-xkiDCjxKjC2DSg1m .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #dependencyStart,#mermaid-svg-xkiDCjxKjC2DSg1m .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #dependencyStart,#mermaid-svg-xkiDCjxKjC2DSg1m .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #extensionStart,#mermaid-svg-xkiDCjxKjC2DSg1m .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #extensionEnd,#mermaid-svg-xkiDCjxKjC2DSg1m .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #aggregationStart,#mermaid-svg-xkiDCjxKjC2DSg1m .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m #aggregationEnd,#mermaid-svg-xkiDCjxKjC2DSg1m .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xkiDCjxKjC2DSg1m .edgeTerminals{font-size:11px;}#mermaid-svg-xkiDCjxKjC2DSg1m :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 ProxyFactory 用来创建代理
如果指定了接口且 proxyTargetClass false使用 JdkDynamicAopProxy如果没有指定接口或者 proxyTargetClass true使用 ObjenesisCglibAopProxy 例外如果目标是接口类型或已经是 Jdk 代理使用 JdkDynamicAopProxy
public class A15 {public static void main(String[] args) {/*两个切面概念aspect 通知1(advice) 切点1(pointcut)通知2(advice) 切点2(pointcut)通知3(advice) 切点3(pointcut)...advisor 更细粒度的切面包含一个通知和切点*/// 1. 备好切点 Pointcut实现AspectJExpressionPointcut pointcut new AspectJExpressionPointcut();pointcut.setExpression(execution(* foo()));// 2. 备好通知 Advice实现MethodInterceptor advice invocation - {System.out.println(before...);Object result invocation.proceed(); // 调用目标System.out.println(after...);return result;};// 3. 备好切面 Advisor实现DefaultPointcutAdvisor advisor new DefaultPointcutAdvisor(pointcut, advice);/*4. 创建代理 jdk or cglib ?a. proxyTargetClass false, 目标实现了接口, 用 jdk 实现b. proxyTargetClass false, 目标没有实现接口, 用 cglib 实现c. proxyTargetClass true, 总是使用 cglib 实现*/Target1 target new Target1();ProxyFactory factory new ProxyFactory();factory.setTarget(target);factory.addAdvisor(advisor);factory.setInterfaces(target.getClass().getInterfaces());factory.setProxyTargetClass(false);I1 proxy (I1) 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);}}
}切点匹配matches 常见 aspectj 切点用法切点为方法或者注解 aspectj 切点的局限性实际的 Transactional 切点实现 Transactional注解 放在方法上表示该方法具有事务能力 放在类上类中所有方法都具有事务能力 放在接口上接口实现类所有方法都具有事务能力
public class A16 {public static void main(String[] args) throws NoSuchMethodException {AspectJExpressionPointcut pt1 new AspectJExpressionPointcut();// matches 判断是否是我们表达式想要的内容 Expression表达式pt1.setExpression(execution(* bar())); // 方法System.out.println(pt1.matches(T1.class.getMethod(foo), T1.class)); // falseSystem.out.println(pt1.matches(T1.class.getMethod(bar), T1.class)); // trueAspectJExpressionPointcut pt2 new AspectJExpressionPointcut();// 注解pt2.setExpression(annotation(org.springframework.transaction.annotation.Transactional));System.out.println(pt2.matches(T1.class.getMethod(foo), T1.class)); // trueSystem.out.println(pt2.matches(T1.class.getMethod(bar), T1.class)); // false/*Transactional注解放在方法上表示该方法具有事务能力放在类上类中所有方法都具有事务能力放在接口上接口实现类所有方法都具有事务能力*/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)); // trueSystem.out.println(pt3.matches(T1.class.getMethod(bar), T1.class)); // falseSystem.out.println(pt3.matches(T2.class.getMethod(foo), T2.class)); // trueSystem.out.println(pt3.matches(T3.class.getMethod(foo), T3.class)); // true/*学到了什么a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法matches的匹配*/}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() {}}
}从 Aspect 到 Advisor高级切面转换为低级切面
AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator BeanPostProcessor 解析处理切面类由该处理器统一转换为低级切面
该处理器类可以作用在依赖注入之前以及初始化之后两个选其一 创建实例化 - (/) 依赖注入 - 初始化 (/) findEligibleAdvisors
findEligibleAdvisors 找到有【资格】的 Advisors 切点包含对应类中方法
有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下advisor3有【资格】的 Advisor 另一部分是高级的, 由解析 Aspect 后获得
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);
// }// 保证同包能调用对应 protected 方法AnnotationAwareAspectJAutoProxyCreator creator context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);ListAdvisor advisors creator.findEligibleAdvisors(Target2.class, target2);for (Advisor advisor : advisors) {System.out.println(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 before...);}After(execution(* foo()))public void before2() {System.out.println(aspect1 after...);}}Configurationstatic 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;}Beanpublic MethodInterceptor advice3() {return invocation - {System.out.println(advice3 before...);Object result invocation.proceed();System.out.println(advice3 after...);return result;};}}}保证类同包为了调用protected权限方法 上述输出是四个Advisor一个默认一个Aspect1类高级切面解析为两个还有一个advisor3低级切面 wrapIfNecessary
内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行 Object o1 creator.wrapIfNecessary(new Target1(), target1, target1);// 有对应Advisor生成代理对象System.out.println(o1.getClass());Object o2 creator.wrapIfNecessary(new Target2(), target2, target2);// 普通对象System.out.println(o2.getClass());// 执行方法会被增强((Target1) o1).foo();代理创建时机 代理的创建时机 初始化之后 (无循环依赖时)实例创建后依赖注入前 (有循环依赖时)并暂存于二级缓存 假设Bean1与Bean2存在循环依赖Bean1初始化过程中会先将Bean1实例化但未初始化的对象放入二级缓存将Bean2对象创建完成再回来继续完善Bean1对象此时Bean2依赖Bean1一定是Bean1代理对象因此代理创建时机提前至实例化后依赖注入前 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
模拟将高级切面转换成低级切面统一转成环绕通知形成通知链 其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用advice)的是一个 MethodInvocation 对象 a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个**【调用链】对象**, 即 MethodInvocation b. 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 适配器类代码很简单实际上就做了一个类型转换 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);}}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. 通知统一转换为环绕通知 MethodInterceptorTarget 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 中的体现*/}
}模拟调用链过程
模拟调用链过程实际上是一个递归过程【责任链模式】
proceed() 方法调用链中下一个环绕通知每个环绕通知内部继续调用 proceed()调用到没有更多通知了, 就调用目标方法结束递归
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; private Method method;private Object[] args;// 环绕通知类列表ListMethodInterceptor methodInterceptorList; private 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 new ArrayList() {{add(new Advice1());add(new Advice2());}};MyInvocation invocation new MyInvocation(target, Target.class.getMethod(foo), new Object[0], list);invocation.proceed();}
}达成效果 静态通知调用
无参数通知切点
代理对象调用流程如下以 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-K6ek4hdCEweF8HoL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-K6ek4hdCEweF8HoL .error-icon{fill:#552222;}#mermaid-svg-K6ek4hdCEweF8HoL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-K6ek4hdCEweF8HoL .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-K6ek4hdCEweF8HoL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-K6ek4hdCEweF8HoL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-K6ek4hdCEweF8HoL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-K6ek4hdCEweF8HoL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-K6ek4hdCEweF8HoL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-K6ek4hdCEweF8HoL .marker.cross{stroke:#333333;}#mermaid-svg-K6ek4hdCEweF8HoL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-K6ek4hdCEweF8HoL .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-K6ek4hdCEweF8HoL text.actortspan{fill:black;stroke:none;}#mermaid-svg-K6ek4hdCEweF8HoL .actor-line{stroke:grey;}#mermaid-svg-K6ek4hdCEweF8HoL .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-K6ek4hdCEweF8HoL .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-K6ek4hdCEweF8HoL #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-K6ek4hdCEweF8HoL .sequenceNumber{fill:white;}#mermaid-svg-K6ek4hdCEweF8HoL #sequencenumber{fill:#333;}#mermaid-svg-K6ek4hdCEweF8HoL #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-K6ek4hdCEweF8HoL .messageText{fill:#333;stroke:#333;}#mermaid-svg-K6ek4hdCEweF8HoL .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-K6ek4hdCEweF8HoL .labelText,#mermaid-svg-K6ek4hdCEweF8HoL .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-K6ek4hdCEweF8HoL .loopText,#mermaid-svg-K6ek4hdCEweF8HoL .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-K6ek4hdCEweF8HoL .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-K6ek4hdCEweF8HoL .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-K6ek4hdCEweF8HoL .noteText,#mermaid-svg-K6ek4hdCEweF8HoL .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-K6ek4hdCEweF8HoL .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-K6ek4hdCEweF8HoL .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-K6ek4hdCEweF8HoL .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-K6ek4hdCEweF8HoL .actorPopupMenu{position:absolute;}#mermaid-svg-K6ek4hdCEweF8HoL .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-K6ek4hdCEweF8HoL .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-K6ek4hdCEweF8HoL .actor-man circle,#mermaid-svg-K6ek4hdCEweF8HoL line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-K6ek4hdCEweF8HoL :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 代理方法执行时会做如下工作
通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知 MethodBeforeAdviceAdapter 将 Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptorAfterReturningAdviceAdapter 将 AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor这体现的是适配器设计模式 所谓静态通知体现在上面方法的 Interceptors 部分这些通知调用时无需再次检查切点直接调用即可结合目标与环绕通知链创建 MethodInvocation 对象通过它完成整个调用体现的是责任链模式
动态通知调用 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);}}带参数通知切点
所谓动态通知体现在上面方法的 DynamicInterceptionAdvice 部分这些通知调用时因为要为通知方法绑定参数还需再次利用切点表达式
带参数切点会被封装成如下对象
class InterceptorAndDynamicMethodMatcher {// 环绕通知final MethodInterceptor interceptor;// 切点final MethodMatcher methodMatcher;public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {this.interceptor interceptor;this.methodMatcher methodMatcher;}}动态通知调用复杂程度高性能较低