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 cglib(spring中)
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 增强方法也不能是 final
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("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");
}
@Override
public 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);
}
}
@Override
public 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);
}
@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 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() {
@Override
public 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);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@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);
}
}
}
和 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() {
@Override
public 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) 编号 2
- save(int) 编号 1
- save() 编号 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");
// 获取代理方法的编号
/*
Proxy
saveSuper() 3
saveSuper(int) 4
saveSuper(long) 5
signature 包括方法名字、参数返回值
*/
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");
// 获取目标方法的编号
/*
Target
save() 0
save(int) 1
save(long) 2
signature 包括方法名字、参数返回值
*/
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
代理相关类图
- AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
- AopProxy 通过 getProxy 创建代理对象
- 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
- 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor
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)); // false
System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class)); // true
AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
// 注解
pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class)); // false
/*
@Transactional注解
放在方法上,表示该方法具有事务能力
放在类上,类中所有方法都具有事务能力
放在接口上,接口实现类所有方法都具有事务能力
*/
StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
@Override
public 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)); // true
System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class)); // false
System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class)); // true
System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class)); // true
/*
学到了什么
a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法
b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法(matches)的匹配
*/
}
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() {
}
}
}
从 @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);
List<Advisor> 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...");
}
}
@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 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. 高级切面转低级切面类
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);
}
/*
@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
a. 通知代码从哪儿来
b. 切点是什么
c. 通知对象如何创建, 本例共用同一个 Aspect 对象
类似的通知还有
1. AspectJAroundAdvice (环绕通知)
2. AspectJAfterReturningAdvice
3. AspectJAfterThrowingAdvice (环绕通知)
4. AspectJAfterAdvice (环绕通知)
*/
// 2. 通知统一转换为环绕通知 MethodInterceptor
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
proxyFactory.addAdvisors(list);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 将不是环绕通知的通知,统一转成环绕通知 形成通知链
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
for (Object o : methodInterceptorList) {
System.out.println(o);
}
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;
// 环绕通知类列表
List<MethodInterceptor> methodInterceptorList;
private int count = 1; // 调用次数,实际上就是切面类方法调用次数
public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
this.target = target;
this.method = method;
this.args = args;
this.methodInterceptorList = methodInterceptorList;
}
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return args;
}
/**
* 调用每一个环绕通知,调用目标 关键!!! 递归调用
*/
@Override
public Object proceed() throws Throwable {
if (count > methodInterceptorList.size()) {
// 所有切面类调用完毕,调用目标方法,返回并结束递归
return method.invoke(target, args);
}
// 逐一调用通知, count + 1
MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
// 执行切面类方法,形成递归调用
return methodInterceptor.invoke(this);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return method;
}
}
public static void main(String[] args) throws Throwable {
Target target = new Target();
List<MethodInterceptor> 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返回最终的结果
(本质是递归调用)
代理方法执行时会做如下工作
- 通过 proxyFactory 的
getInterceptorsAndDynamicInterceptionAdvice()
将其他通知统一转换为 MethodInterceptor 环绕通知
- MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
- AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
- 这体现的是适配器设计模式
- 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可
- 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用,体现的是责任链模式
动态通知调用
@Aspect
static class MyAspect {
@Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象
public void before2(int x) {
System.out.printf("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;
}
}
- 动态通知调用复杂程度高,性能较低