Aspect
定义切面(切面=切入点+通知)
@Component // 交由spring管理
@Aspect // 切面
public class MyAspect {
// 切入点 所有foo方法
@Before("execution(* foo(..))")
public void before() {
// 通知
System.out.println("前置通知");
}
@After("execution(* foo(..))")
public void after() {
// 通知
System.out.println("后置通知");
}
}
待增强的方法(被代理的目标对象)
@Component
public class MyService {
public void foo() {
System.out.println("MyService.foo");
}
}
测试
ConfigurableApplicationContext applicationContext = SpringApplication.run(TestAspect.class, args);
MyService myService = applicationContext.getBean(MyService.class);
myService.foo();
Advisor
待增强的方法(被代理的目标对象)
interface I1 {
void foo();
void bar();
}
class Target1 implements I1 {
@Override
public void foo() {
System.out.println("Target1.foo");
}
@Override
public void bar() {
System.out.println("Target1.bar");
}
}
class Target2 {
public void foo() {
System.out.println("Target2.foo");
}
public void bar() {
System.out.println("Target2.bar");
}
}
测试
// 1. 定义切入点 表达式切入点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 定义通知
MethodInterceptor advice = new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
System.out.println("before...");
// 调用目标方法
Object ret = invocation.proceed();
System.out.println("after...");
return ret;
}
};
// 3. 定义切面 封装切入点与通知
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
// 4. 创建代理
Target1 target = new Target1();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.setProxyTargetClass(true);
proxyFactory.setInterfaces(target.getClass().getInterfaces());
// 添加切面
proxyFactory.addAdvisor(advisor);
/**
* 生成代理的方式是jdk还是cglib 由spring内部决定
* 规则如下:
* a. proxyTargetClass为false (ProxyConfigs属性 proxyFactory.setProxyTargetClass(false))
* 目标对象实现了接口 (proxyFactory.setInterfaces(target.getClass().getInterfaces());) 【使用JDK】
* b. proxyTargetClass为false 目标对象没有实现接口 【使用cglib】
* c. proxyTargetClass为true 【使用cglib】 @EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
*/
Target1 proxy = (Target1) proxyFactory.getProxy();
/**
* JDK:
* class com.you.meet.nice.test.web.spring.aop.spring.$Proxy2
* CGLIB:
* class com.you.meet.nice.test.web.spring.aop.spring.Target2$$EnhancerBySpringCGLIB$$818f8618
*/
System.out.println("proxyClass = " + proxy.getClass());
proxy.foo();
proxy.bar();
Aspect与Advisor关系
:::info
spring定义切面两种方式 aspect/advisor
1. aspect可以定义多组通知和切入点,advisor只能定义一组通知和切入点
2. aspect底层还是advisor
:::
切入点匹配
准备目标对象
static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}
@Transactional
static class T2 {
public void foo() {
}
public void bar() {
}
}
@Transactional
interface I1 {
void foo();
}
static class T3 implements I1 {
@Override
public void foo() {
}
public void bar() {
}
}
AspectJExpressionPointcut匹配
AspectJExpressionPointcut pt = new AspectJExpressionPointcut();
// 匹配方法
pt.setExpression("execution(* foo())");
System.out.println("匹配方法:" + pt.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println("匹配方法:" + pt.matches(T1.class.getMethod("bar"), T1.class)); // false
pt = new AspectJExpressionPointcut();
// 匹配注解
pt.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println("匹配方法注解:" + pt.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println("匹配方法注解:" + pt.matches(T1.class.getMethod("bar"), T1.class)); // false
StaticMethodMatcherPointcut匹配
:::info
那Spring底层是不是就是使用AspectJExpressionPointcut在匹配事务注解的呢?
答案:不是的
因为事务注解不仅可以放在方法上,还可以放在类上,接口上(表示类/接口实现类里的所有方法都包含事务)
而AspectJExpressionPointcut只能匹配方法,Spring底层是使用StaticMethodMatcherPointcut匹配的
:::
StaticMethodMatcherPointcut transPointcut = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 先判断方法上是否包含事务注解
MergedAnnotations mergedAnnotations = MergedAnnotations.from(method);
if (mergedAnnotations.isPresent(Transactional.class)) {
return true;
}
// 默认只会找当前类的注解 不会查找这个类继承树上其他父类是否存在注解
// mergedAnnotations = MergedAnnotations.from(targetClass);
// 找到这个类以及继承树上是否有此注解
mergedAnnotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (mergedAnnotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};
System.out.println("匹配方法/类注解:" + transPointcut.matches(T1.class.getMethod("foo"), T1.class)); // true
System.out.println("匹配方法/类注解:" + transPointcut.matches(T1.class.getMethod("bar"), T1.class)); // false
System.out.println("匹配方法/类注解:" + transPointcut.matches(T2.class.getMethod("foo"), T2.class)); // true
System.out.println("匹配方法/类注解:" + transPointcut.matches(T2.class.getMethod("bar"), T2.class)); // true
System.out.println("匹配方法/类注解:" + transPointcut.matches(T3.class.getMethod("foo"), T3.class)); // true
System.out.println("匹配方法/类注解:" + transPointcut.matches(T3.class.getMethod("bar"), T3.class)); // true
AnnotationAwareAspectJAutoProxyCreator
Spring是如何生成代理类的?
准备目标对象
static class Target1 {
public void foo() {
System.out.println("Target1.foo");
}
}
static class Target2 {
public void bar() {
System.out.println("Target2.bar");
}
}
准备切面
@Aspect // 高级切面
static class Aspect1 {
@Before("execution(* foo())")
public void before() {
System.out.println("Aspect1.before");
}
@After("execution(* foo())")
public void after() {
System.out.println("Aspect1.after");
}
}
@Configuration
static class Config {
@Bean
public MethodInterceptor advice1() {
return new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
System.out.println("advisor1.invoke before...");
Object ret = invocation.proceed();
System.out.println("advisor1.invoke after...");
return ret;
}
};
}
@Bean // 低级切面
public Advisor advisor1(Advice advice1) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice1);
}
}
测试
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean("aspect1", Aspect1.class);
applicationContext.registerBean(Config.class);
applicationContext.registerBean(ConfigurationClassPostProcessor.class);
/**
* BeanPostProcessor: 找到所有切面 如果是高级切面需要转换为低级切面 最后生成代理对象
* 初始化后执行
*/
applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
applicationContext.refresh();
重要方法
findEligibleAdvisors:收集适用指定bean的所有切面
AnnotationAwareAspectJAutoProxyCreator proxyCreator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 找到所有有资格的切面 高级切面转换为低级切面
Method findEligibleAdvisorsMethod = AbstractAdvisorAutoProxyCreator.class.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
findEligibleAdvisorsMethod.setAccessible(true);
// 收集所有适用Target1的切面
List<Advisor> advisors = (List<Advisor>) findEligibleAdvisorsMethod.invoke(proxyCreator, Target1.class, "target1");
for (Advisor advisor : advisors) {
// 除了spring内置的ExposeInvocationInterceptor 还有三个切面 高级切面转换为2个低级切面+1个低级切面
System.out.println("advisor = " + advisor);
}
wrapIfNecessary:生成代理类
Method wrapIfNecessaryMethod = AbstractAutoProxyCreator.class.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
wrapIfNecessaryMethod.setAccessible(true);
// spring使用容器中取 这里直接实例化测试
Object target1 = wrapIfNecessaryMethod.invoke(proxyCreator, new Target1(), "target1", "target1");
Object target2 = wrapIfNecessaryMethod.invoke(proxyCreator, new Target2(), "target2", "target2");
// 匹配到切点表达式 生成cglib代理
System.out.println("target1.getClass() = " + target1.getClass());
// 未匹配到 原对象
System.out.println("target2.getClass() = " + target2.getClass());
((Target1) target1).foo();
代理创建时机
Bean
static class Bean1 {
public Bean1() {
System.out.println(this.getClass() + " invoke Bean1.Bean1()");
}
@Autowired // 验证循环依赖
public void setBean2(Bean2 bean2) {
System.out.println(this.getClass() + "Bean1.setBean2()->" + bean2.getClass());
}
@PostConstruct
public void init() {
System.out.println(this.getClass() + " Bean1.init()");
}
public void foo() {
System.out.println("Bean1.foo");
}
}
static class Bean2 {
public Bean2() {
System.out.println(this.getClass() + " invoke Bean2.Bean2()");
}
@Autowired
public void setBean1(Bean1 bean1) {
// bean1是代理对象
System.out.println(this.getClass() + " Bean2.setBean1()-> " + bean1.getClass());
}
@PostConstruct
public void init() {
System.out.println(this.getClass() + " Bean2.init()");
}
}
配置类
@Configuration
static class Config {
@Bean
public MethodInterceptor fooAdvice() {
return new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
System.out.println("before...");
invocation.proceed();
System.out.println("after...");
return null;
}
};
}
@Bean
public Advisor fooAdvisor(Advice fooAdvice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, fooAdvice);
}
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
测试
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean(Config.class);
// 处理注解bean
applicationContext.registerBean(ConfigurationClassPostProcessor.class);
// 处理注解生成代理
applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 处理依赖注入
applicationContext.registerBean(AutowiredAnnotationBeanPostProcessor.class);
// 处理初始化@PostConstruct
applicationContext.registerBean(CommonAnnotationBeanPostProcessor.class);
applicationContext.refresh();
Bean1 bean = applicationContext.getBean(Bean1.class);
bean.foo();
总结
实例化 -> () -> 依赖注入 -> 初始化 -> ()
代理的创建时机:
- 初始化之后:没有循环依赖
- 实例化后,依赖注入之前(有循环依赖) 暂存于二级缓存
依赖注入和初始化不应该被增强,即还是原对象调用
切面顺序
默认顺序
以前面章节AnnotationAwareAspectJAutoProxyCreator中的例子描述,切面的默认顺序是
低级切面(Advisor) > 高级切面(@Aspect)
调整顺序
可以使用@Order注解调整顺序
顺序失效情况
DefaultPointcutAdvisor顺序不能使用@Order使用,只能调用advisor.setOrder。
通知无法控制顺序
高级切面是怎么转换为低级切面的?
准备高级切面与通知
static class Aspect {
@Before("execution(* foo())")
public void before() {
System.out.println("Aspect.before");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("Aspect.before2");
}
public void after() {
System.out.println("Aspect.after");
}
public void afterReturning() {
System.out.println("Aspect.afterReturning");
}
public void afterThrowing() {
System.out.println("Aspect.afterThrowing");
}
public void around() {
System.out.println("Aspect.around");
}
}
static class Target {
public void foo() {
System.out.println("Target.foo");
}
}
测试
// 切面实例工厂
AspectInstanceFactory aif = new SingletonAspectInstanceFactory(new Aspect());
// 切面
List<Advisor> advisorList = 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 beforeAdvice = new AspectJMethodBeforeAdvice(method, pointcut, aif);
//AspectJAroundAdvice 环绕
//AspectJAfterReturningAdvice 返回
//AspectJAfterThrowingAdvice 异常
//AspectJAfterAdvice 后置
// 切面
advisorList.add(new DefaultPointcutAdvisor(pointcut, beforeAdvice));
}
}
advisorList.forEach(System.out::println);
非环绕通知转换为环绕通知
环绕通知:即实现了MethodInterceptor的通知
环绕通知有:
- AspectJAroundAdvice
- AspectJAfterAdvice
- AspectJAfterThrowingAdvice
非环绕通知有
- AspectJMethodBeforeAdvice
- AspectJAfterReturningAdvice
static class Aspect {
@Before("execution(* foo())")
public void before() {
System.out.println("Aspect.before");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("Aspect.before2");
}
public void after() {
System.out.println("Aspect.after");
}
@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("Aspect.afterReturning");
}
@AfterThrowing("execution(* foo())")
public void afterThrowing() {
System.out.println("Aspect.afterThrowing");
}
public void around() {
System.out.println("Aspect.around");
}
}
// 切面实例工厂
AspectInstanceFactory aif = new SingletonAspectInstanceFactory(new Aspect());
// 切面
List<Advisor> advisorList = 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 beforeAdvice = new AspectJMethodBeforeAdvice(method, pointcut, aif);
//AspectJAroundAdvice 环绕
//AspectJAfterReturningAdvice 返回
//AspectJAfterThrowingAdvice 异常
//AspectJAfterAdvice 后置
// 切面
advisorList.add(new DefaultPointcutAdvisor(pointcut, beforeAdvice));
} else if (method.isAnnotationPresent(AfterReturning.class)) {
// 解析切入点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// return通知
AspectJAfterReturningAdvice afterReturningAdvice = new AspectJAfterReturningAdvice(method, pointcut, aif);
// 切面
advisorList.add(new DefaultPointcutAdvisor(pointcut, afterReturningAdvice));
} else if (method.isAnnotationPresent(AfterThrowing.class)) {
// 解析切入点
String expression = method.getAnnotation(AfterThrowing.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 异常通知
AspectJAfterThrowingAdvice afterThrowingAdvice = new AspectJAfterThrowingAdvice(method, pointcut, aif);
// 切面
advisorList.add(new DefaultPointcutAdvisor(pointcut, afterThrowingAdvice));
}
}
// 转换前
advisorList.forEach(System.out::println);
// 这里会有一个排序操作
// 非环绕通知转换为环绕通知
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new Target());
proxyFactory.addAdvisors(advisorList);
System.out.println("============>");
// 转换为环绕通知
List<Object> dynamicInterceptionAdvice = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
dynamicInterceptionAdvice.forEach(System.out::println);
适配器
这种转换体现了适配器模式的思想
- MethodBeforeAdviceAdapter
@Before AspectJMethodBeforeAdvice -> MethodBeforeAdviceAdapter
- AfterReturningAdviceAdapter
@AfterReturning AspectJAfterReturningAdvice -> AfterReturningAdviceInterceptor
调用链执行所有通知以及目标方法
// 非环绕通知转换为环绕通知
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
// 将ExposeInvocationInterceptor放入第一个通知 将MethodInvocation放入线程上下文
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
proxyFactory.addAdvisors(advisorList);
System.out.println("============>");
// 转换为环绕通知 适配器模式
Method fooMethod = Target.class.getMethod("foo");
List<Object> dynamicInterceptionAdvice = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(fooMethod, Target.class);
dynamicInterceptionAdvice.forEach(System.out::println);
// 创建调用链 (执行所有环绕通知和目标方法)
Constructor<ReflectiveMethodInvocation> reflectiveMethodInvocationConstructor = ReflectUtil.getConstructor(ReflectiveMethodInvocation.class, Object.class, Object.class, Method.class, Object[].class,
Class.class, List.class);
reflectiveMethodInvocationConstructor.setAccessible(true);
ReflectiveMethodInvocation reflectiveMethodInvocation = reflectiveMethodInvocationConstructor.newInstance(
null, target, fooMethod, new Object[0], Target.class, dynamicInterceptionAdvice
);
/**
* 会报错:因为其它通知方法里面可能也需要MethodInvocation对象
* 所以需要使用ExposeInvocationInterceptor将MethodInvocation放到线程上下文
*/
reflectiveMethodInvocation.proceed();
模拟调用链
调用链: 递归+责任链
准备目标以及通知
static class Target {
public void foo() {
System.out.println("Target.foo");
}
}
static class Advice1 implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
System.out.println("Advice1.invoke before...");
// 调用下一个通知或者目标方法
Object ret = invocation.proceed();
System.out.println("Advice1.invoke after...");
return ret;
}
}
static class Advice2 implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
System.out.println("Advice2.invoke before...");
Object ret = invocation.proceed();
System.out.println("Advice2.invoke after...");
return ret;
}
}
调用链对象
static class MyMethodInvocation implements MethodInvocation {
private Object target; // 1
private Method targetMethod;
private Object[] targetArgs;
private List<MethodInterceptor> methodInterceptorList; // 2
private int callCount = 1;
public MyMethodInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
this.target = target;
this.targetMethod = method;
this.targetArgs = args;
this.methodInterceptorList = methodInterceptorList;
}
@Nonnull
@Override
public Method getMethod() {
return targetMethod;
}
@Nonnull
@Override
public Object[] getArguments() {
return targetArgs;
}
@Nullable
@Override
public Object proceed() throws Throwable {
// 如果还有环绕通知 调用环绕通知 没有的话 调用目标方法
if (callCount > methodInterceptorList.size()) {
return targetMethod.invoke(target, targetArgs);
}
// 逐个递归调用环绕通知 调用次数+1
MethodInterceptor methodInterceptor = methodInterceptorList.get(callCount - 1);
callCount++;
return methodInterceptor.invoke(this);
}
@Nullable
@Override
public Object getThis() {
return target;
}
@Nonnull
@Override
public AccessibleObject getStaticPart() {
return targetMethod;
}
}
测试
Target target = new Target();
List<MethodInterceptor> adviceList = CollUtil.newArrayList(new Advice1(), new Advice2());
MyMethodInvocation methodInvocation = new MyMethodInvocation(target, Target.class.getMethod("foo"), new Object[0], adviceList);
methodInvocation.proceed();
动态通知
目标对象与通知
static class Target {
public void foo(int x) {
System.out.println("Target.foo.int." + x);
}
}
@org.aspectj.lang.annotation.Aspect
static class Aspect {
@Before("execution(* foo(..))") // 静态通知调用 不需要参数绑定 执行时不需要切点
public void before() {
System.out.println("Aspect.before");
}
@Before("execution(* foo(..)) && args(x)") // 动态通知调用 需要进行参数绑定 执行时需要切点对象
public void before(int x) {
System.out.println("Aspect.before.int." + x);
}
}
测试
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean(Aspect.class);
applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
applicationContext.refresh();
AnnotationAwareAspectJAutoProxyCreator proxyCreator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
Method findEligibleAdvisorsMethod = AbstractAdvisorAutoProxyCreator.class.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
findEligibleAdvisorsMethod.setAccessible(true);
List<Advisor> advisors = (List<Advisor>) findEligibleAdvisorsMethod.invoke(proxyCreator, TestDynamicAdvice.Target.class, "target");
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisors(advisors);
// 获取代理
Object proxy = proxyFactory.getProxy();
Method fooMethod = Target.class.getMethod("foo", int.class);
List<Object> methodInterceptors = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(fooMethod, Target.class);
for (Object methodInterceptor : methodInterceptors) {
/**
* ExposeInvocationInterceptor: 将调用链对象存入线程上下文
* MethodBeforeAdviceInterceptor 前置通知(已转换环绕通知)
* InterceptorAndDynamicMethodMatcher 动态通知对象 持有MethodInterceptor(环绕通知) + methodMatcher(切入点) 对象
*/
System.out.println(methodInterceptor);
}
System.out.println("==============>");
Constructor<ReflectiveMethodInvocation> reflectiveMethodInvocationConstructor = ReflectUtil.getConstructor(ReflectiveMethodInvocation.class, Object.class, Object.class, Method.class, Object[].class,
Class.class, List.class);
reflectiveMethodInvocationConstructor.setAccessible(true);
ReflectiveMethodInvocation reflectiveMethodInvocation = reflectiveMethodInvocationConstructor.newInstance(
proxy, target, fooMethod, new Object[]{100}, Target.class, methodInterceptors
);
reflectiveMethodInvocation.proceed();
小结
:::info
有参数绑定的通知调用时还需要切入点,对参数进行匹配和绑定�
动态通知复杂度高,性能要比静态通知要低�
:::