- 作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家
- 系列专栏:Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术
- 如果感觉博主的文章还不错的话,请三连支持一下博主哦
- 博主正在努力完成2023计划中:源码溯源,一探究竟
- 联系方式:nhs19990716,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
拿之前介绍过的代码举例:
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. 高级切面转低级切面类
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);
}
输出结果为
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object
org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void
org.springframework.aop.framework.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.afterReturning()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before2()]; aspect name '']
其余的和前面介绍的都一样,只是又额外添加了after 和 around的改写
但是实际上无论after 还是 before都会转换为 MethodInterceptor
其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象
a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocation
b. MethodInvocation 要知道 advice 有哪些, 还要知道目标, 调用次序如下
将 MethodInvocation 放入当前线程
|-> before1 ----------------------------------- 从当前线程获取 MethodInvocation
| |
| |-> before2 -------------------- | 从当前线程获取 MethodInvocation
| | | |
| | |-> target ------ 目标 advice2 advice1
| | | |
| |-> after2 --------------------- |
| |
|-> after1 ------------------------------------
从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知
统一转换为环绕通知, 体现的是设计模式中的适配器模式(将一种接口转换成另一种接口,供调用 )
此步获取所有执行时需要的 advice (静态)
a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上
b. 适配如下
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);
}
输出结果:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.interceptor.ExposeInvocationInterceptor@70e9c95d
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@4d5650ae
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@a38c7fe
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@6fdbe764
其输出的都被转化成了环绕通知,都实现了MethodInterceptor
// 3. 创建并执行调用链 (环绕通知s + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
methodInvocation.proceed();
输出结果:
around...before
before1
before2
target foo
afterReturning
around...after
那么调用链是怎么实现的呢?
模拟调用链过程, 是一个简单的递归过程
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; // 1
private Method method;
private Object[] args;
List<MethodInterceptor> methodInterceptorList; // 2
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 = List.of(
new Advice1(),
new Advice2()
);
MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
invocation.proceed();
}
最终执行输出:
Advice1.before()
Advice2.before()
Target.foo()
Advice2.after()
Advice1.after()
前面都是介绍静态的通知调用,那么下面来说说动态通知调用
@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);
}
}
static class Target {
public void foo(int x) {
System.out.printf("target foo(%d)%n", x);
}
}
@Configuration
static class MyConfig {
@Bean
AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean
public 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);
List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");
Target target = new Target();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisors(list);
Object proxy = factory.getProxy(); // 获取代理
// 得到的环绕通知集合
List<Object> 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();
}
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();
}
}
最终结果为:
before1
before2(100)
target foo(100)