第十七讲-Spring中的两种切面
第十七讲-Spring中的两种切面
Spring种有两种切面,一种是高级的@Aspect
切面,还有一种是低级的Advisor
切面,这里的低级指的是比较底层的切面。对于高级切面来讲,@Aspect
切面最终都会转为低级切面才会被Spring框架所使用。
如下面的演示:
package com.cherry.chapter1.a17;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
public class A17 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// 注册几个切面类
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config",Config.class);
// 加入bean工厂后处理器解析@Bean
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String beanName : context.getBeanDefinitionNames()){
System.out.println(beanName);
}
}
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 前置通知...");
}
@After("execution(* foo())")
public void after(){
System.out.println("aspect1 前后置通知...");
}
}
// 创建一个低等级切面类(使用配置类的方式)
@Configuration
static class Config {
// 提供一个低等级的切面类
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advise3){
// 定义一个切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advise3);
}
@Bean
public MethodInterceptor advise3(){
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("advise3 前后置通知...");
Object result = invocation.proceed();
System.out.println("advise3 前后置通知...");
return result;
}
};
}
}
}
aspect1
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
advisor3
advise3
我们发现此时的容器中有我们定义的切面类对象,配置类,低级的切面advisor3以及低级切面对应的通知advise3。那么在Spring种这些切面是怎么被找到的呢?即使找到了又是怎么区分高级切面和低级切面的呢?而且不是说高级切面不是最终会转换成低级切面的吗?最终是怎么转换的呢?除此之外,找到切面类后又是如何创建代理对象的呢?接下来我们来分析一下:
完成上面疑问的类,是一个BeanPostProcessor, 该类叫AnnotationAwareAspectJAutoProxyCreator
,我们把这个后处理器加入到beanFactory中:
// 用于处理AOP的后处理器
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
这里呢,我们来大致看一下AnnotationAwareAspectJAutoProxyCreator
源码
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
// .....................................
// 找到一些有资格的切面类,该方法找到的是低级切面,如果是找到了高级切面,则会将高级切面转为低级切面再加入到eligibleAdvisors集合中
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
this.extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
// 该方法是创建代理的方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
// .....................
}
注意,由于上述的两种方法都是受保护的,使用反射调用太麻烦了,因此我们可以将我们的测试代码放到和AnnotationAwareAspectJAutoProxyCreator
同一个包下,如下面的截图:
// 第一个重要的方法findEligibleAdvisors就是所有[有资格]的advisors
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findCandidateAdvisors(Target1.class, "target1");// 根据目标来判断是否具备的advisors
advisors.forEach(advisor -> System.out.println(advisor));
我们发现一共有4个切面,第一个是Spring给所有代理都加的切面,剩下三个都是我们自己写的,包括低级切面和转为低级切面的高级切面。
接下来我们看一下AnnotationAwareAspectJAutoProxyCreator
处理器的第二个方法:wrapIfNecessary:是否有必要为目标创建代理,只要findEligibleAdvisors返回的集合不为空,则表示需要创建代理
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o1.getClass());
System.out.println(o2.getClass());
我们发现,Spring为Target1创建了代理对象,并未Target2创建代理对象,因为bar()方法和切点并未匹配,因此该类并不需要创建代理了。
现在我们来看一下代理类的创建时机,之前我们提过,Bean的创建时机有:创建实例 -> (*)依赖注入 -> 初始化(*)。
代理类的创建时机有两个位置,一个是在创建实例和依赖注入之间,另一个位置是在初始化之后,这两个位置是二选一的。这里先说结论:出现循环依赖会提前创建代理对象,否则会在初始化后创建代理对象。下面呢,我们验证一下:
package org.springframework.aop.framework.autoproxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.PostConstruct;
public class A17_1 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(Config.class);
context.refresh();
context.close();
// 创建 -> (*) 依赖注入 -> 初始化 (*)
/*
学到了什么
a. 代理的创建时机
1. 初始化之后 (无循环依赖时)
2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
*/
}
@Configuration
static class Config {
@Bean // 解析 @Aspect、产生代理
public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
@Bean // 解析 @Autowired
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
return new AutowiredAnnotationBeanPostProcessor();
}
@Bean // 解析 @PostConstruct
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
public void foo() {
}
public Bean1() {
System.out.println("Bean1()");
}
//@Autowired public void setBean2(Bean2 bean2) {
// System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
// }
@PostConstruct public void init() {
System.out.println("Bean1 init()");
}
}
static class Bean2 {
public Bean2() {
System.out.println("Bean2()");
}
@Autowired public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}
@PostConstruct public void init() {
System.out.println("Bean2 init()");
}
}
}
我们发现,首先打印了Bean1的构造,然后是Bean1的初始化,之后有一条关键的日志。这是代理创建器打印的日志:为Bean1创建了代理,因此是在Bean对象初始化之后创建了代理。
紧接着执行了Bean2的构造方法,并需要一个Bean1对象,那么是将Bean1的原始对象给它还是将Bean1的代理对象给它呢?肯定要给代理对象,因为Bean2调用使用Bean1对象肯定会调用Bean1的方法,当然是希望调用Bean1中增强的方法。因此我们给Bean2注入的是Bean1的代理对象,紧接着Bean2完成了初始化。
接着我们打开注释重新运行,现在Bean1和Bean2是双向相互引用的关系(循环依赖),我们再次运行看一下:
我们发现,首先调用了Bean1的构造方法,此时要进行依赖注入阶段,而此时的依赖注入需要Bean2,但现在并没有Bean2对象,因此会暂停Bean1的后续步骤,进入Bean2的流程,
接下来调用了Bean2的构造方法,但是此时的Bean2d的依赖注入需要Bean1,而Bean1现在也没有初始化好。因为卡在了依赖注入阶段。因此,这个Bean1的代理就会在Bean2注入之前被创建出来,因此代理对象会提前创建出来,因此我们发现:存在循环引用的情况下,代理对象在构造方法之后,依赖注入之前会创建出来,其目的就是给需要的Bean使用,最后完成注入阶段和初始化阶段。
这里我们总结一下:
代理创建的时机有两种:
- 一种是在初始化之后(无循环依赖时)
- 另一种是构造方法执行之后,依赖注入之前(有循环依赖时),会将代理提前创建出来,并暂存于二级缓存中。
此外,在依赖注入和初始化阶段不应该被增强,应该施加于原始对象上
接下来我们讲一下切面的顺序控制
如果有多个切面的时候,它们的顺序是怎么样的呢?我们看一下下面的代码:
package org.springframework.aop.framework.autoproxy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;
import java.util.List;
public class A17 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// BeanPostProcessor
// 创建 -> (*) 依赖注入 -> 初始化 (*)
context.refresh();
// for (String name : context.getBeanDefinitionNames()) {
// System.out.println(name);
// }
/*
第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
a. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3
b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 @Aspect 后获得
*/
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
/*for (Advisor advisor : advisors) {
System.out.println(advisor);
}*/
/*
第二个重要方法 wrapIfNecessary
a. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
*/
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());
((Target1) o1).foo();
/*
学到了什么
a. 自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator 会帮我们创建代理
b. 通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
c. 高级的 @Aspect 切面会转换为低级的 Advisor 切面, 理解原理, 大道至简
*/
}
static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}
@Aspect // 高级切面类
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
}
运行如下:
我们发现,低级切面的advice3是在外层,说明它优先被执行。我们可以看到:低级切面优先被执行,其次是高级切面,这个切面的执行顺序我们能不能控制呢?我们可以加@Order
注解来排序(数字小的优先级高一些)。现在我们在Aspect1上用Order注解标识,如下面所示:
package org.springframework.aop.framework.autoproxy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;
import java.util.List;
public class A17 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// BeanPostProcessor
// 创建 -> (*) 依赖注入 -> 初始化 (*)
context.refresh();
// for (String name : context.getBeanDefinitionNames()) {
// System.out.println(name);
// }
/*
第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
a. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3
b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 @Aspect 后获得
*/
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
/*for (Advisor advisor : advisors) {
System.out.println(advisor);
}*/
/*
第二个重要方法 wrapIfNecessary
a. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
*/
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());
((Target1) o1).foo();
/*
学到了什么
a. 自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator 会帮我们创建代理
b. 通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行
c. 高级的 @Aspect 切面会转换为低级的 Advisor 切面, 理解原理, 大道至简
*/
}
static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}
}
此时定义的高级切面被优先调用了!
最后我们讲讲高级切面如何转为低级切面。
如果我们使用findEligibleAdvisors
方法查找的是高级切面怎么办呢?这里会有一个转换,将Aspect切面转为Advisor切面,这个过程就涉及到@Aspect注解的解析,这里我们大致研究一下注解的解析过程,这里以@Before为例,其它注解类似:
package org.springframework.aop.framework.autoproxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class A17_2 {
static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
public void after() {
System.out.println("after");
}
public void afterReturning() {
System.out.println("afterReturning");
}
public void afterThrowing() {
System.out.println("afterThrowing");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}
static class Target {
public void foo() {
System.out.println("target foo");
}
}
@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 准备一个存放切面的集合
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
// 判断方法上有没有@Before注解
if (method.isAnnotationPresent(Before.class)) {
// 拿到注解,并获取到切点表达式(@Before注解中的值)
String expression = method.getAnnotation(Before.class).value();
// 构造一个切点对象
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// 将从注解中解析到的切点表达式值设置到切点表达式对象中
pointcut.setExpression(expression);
// 准备一个通知方法对象
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 准备一个切面类对象,并将切点对象和通知方法对象传递进去,此时就完成了高级切面到低级切面的转换
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
// 将该切面对象加入到集合中
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
/*
@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
a. 通知代码从哪儿来
b. 切点是什么(这里为啥要切点, 后面解释)
c. 通知对象如何创建, 本例共用同一个 Aspect 对象
类似的通知还有
1. AspectJAroundAdvice (环绕通知)
2. AspectJAfterReturningAdvice
3. AspectJAfterThrowingAdvice
4. AspectJAfterAdvice (环绕通知)
*/
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构