layout | title | date | categories | tags | series | series_index | comments | copyrights | mathjax |
---|---|---|---|---|---|---|---|---|---|
post |
AOP |
2024-02-02 00:00:00 +0800 |
编程 |
java spring |
深入 Spring 源码 |
2 |
true |
原创 |
true |
Spring 是一个开源的轻量级 JavaEE 框架。它的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 的 IoC 容器负责管理 JavaBean 的生命周期,而 AOP 容器负责管理切面。Spring 还提供了一系列的模块,如 Spring MVC、Spring JDBC、Spring Security 等。
我们再次回顾一下三层架构:
在实际开发中,我们的业务逻辑并不是完全自上而下的,日志、事务、权限控制等横向贯穿在各个层次之间。这些辅助功能被成为切面(Aspect)。
在严格的 OOP 中,如果要向业务代码中添加日志功能,需要这样做:
@Service
public class CalculatorServiceImpl implements CalculatorService {
@Autowired
private CalculatorDao calculatorDao;
public int add(int a, int b) {
System.out.println("[INFO] add method start");
int result = calculatorDao.add(a, b);
System.out.println("[INFO] add method end");
return result;
}
public int subtract(int a, int b) {
System.out.println("[INFO] subtract method start");
int result = calculatorDao.subtract(a, b);
System.out.println("[INFO] subtract method end");
return result;
}
/* ... */
}
可以看到,这样做有几个问题:
-
代码冗余
每个方法都需要写高度雷同日志代码,导致了代码冗余。
-
耦合度高
业务代码和日志代码耦合在一起,导致了耦合度过高,不利于集中维护。
-
横切关注点
日志代码是横切关注点,它贯穿在各个方法中,但是却和业务逻辑无关。
AOP 的目的就是解决这些问题。它将横切关注点从业务代码中剥离出来,使得业务代码更加简洁、清晰。
它和 Python 中的装饰器有点类似,都是对原先的代码进行进一步包装,在其运行前、运行后、运行中额外执行一些代码。
-
横切关注点(Cross-cutting Concern)
横切关注点是指那些和业务逻辑无关,但是贯穿在各个方法中的代码。例如日志、事务、权限控制等。
-
增强(Advice)
增强是指在横切关注点中需要执行的代码。例如输出日志、开启事务、检查权限等。它有以下几种类型:
- 前置增强(Before):在目标方法执行前执行
- 后置增强(After):在目标方法执行后执行
- 返回增强(After Returning):在目标方法返回结果后执行
- 异常增强(After Throwing):在目标方法抛出异常后执行
- 环绕增强(Around):在目标方法执行前后执行,包括了前面四种增强
-
切面(Aspect)
切面是横切关注点和增强结合而成的类。
-
目标对象(Target)
目标对象是被增强的对象。例如
CalculatorServiceImpl
。 -
代理对象(Proxy)
代理对象是 Spring 生成的代理对象,它将目标对象和切面结合在一起。
-
连接点(Join Point)
连接点是指在程序执行过程中能够插入切面的点。例如方法调用、方法执行、异常抛出等。
-
切入点(Pointcut)
切入点是指用于定位连接点的表达式。
在讲解 AOP 之前,我们先来看看代理模式。
在静态代理中,代理类和目标类实现了同一个接口,代理类中持有目标类的引用,通过调用目标类的方法来实现代理:
public interface CalculatorService {
int add(int a, int b);
}
public class CalculatorServiceImpl implements CalculatorService {
public int add(int a, int b) {
return a + b;
}
}
public class CalculatorServiceProxy implements CalculatorService {
private CalculatorService calculatorService;
public CalculatorServiceProxy(calculatorService) {
this.calculatorService = calculatorService;
}
public int add(int a, int b) {
System.out.println("[INFO] add method start");
int result = calculatorService.add(a, b);
System.out.println("[INFO] add method end");
return result;
}
}
public class TestCalculator {
@Test
public void testCalculator() {
CalculatorService calculatorService = new CalculatorServiceImpl();
CalculatorService calculatorServiceProxy = new CalculatorServiceProxy(calculatorService);
int result = calculatorServiceProxy.add(1, 2);
}
}
这种方法只能代理一个接口,如果有多个接口需要代理,就需要写多个代理类。
在动态代理中,代理类和目标类没有实现同一个接口,代理类通过实现 InvocationHandler
接口来实现代理:
public interface CalculatorService {
int add(int a, int b);
}
public class CalculatorServiceImpl implements CalculatorService {
public int add(int a, int b) {
return a + b;
}
}
public class CalculatorServiceProxy implements InvocationHandler {
private Object target;
public CalculatorServiceProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[INFO] " + method.getName() + " method start, args: " + Arrays.toString(args));
Object result = method.invoke(target, args);
System.out.println("[INFO] " + method.getName() + " method end");
return result;
}
}
public class TestCalculator {
@Test
public void testCalculator() {
CalculatorService calculatorService = new CalculatorServiceImpl();
CalculatorService calculatorServiceProxy = (CalculatorService) Proxy.newProxyInstance(
calculatorService.getClass().getClassLoader(),
calculatorService.getClass().getInterfaces(),
new CalculatorServiceProxy(calculatorService)
);
int result = calculatorServiceProxy.add(1, 2);
}
}
这里的 Proxy.newProxyInstance
方法会返回一个代理对象,它实现了 CalculatorService
接口,通过 CalculatorServiceProxy
来实现代理。
在 Spring 中,AOP 使用的就是动态代理。它通过将以上内容封装进一个类,来实现代理。
Spring 提供了两种 AOP 使用方式:基于 XML 配置和基于注解配置。我们先来看看基于注解配置的方式。
首先,我们需要在配置类中开启 AOP:
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
这里,@EnableAspectJAutoProxy
注解用于开启 AOP,它会自动扫描 @Aspect
注解标记的类,并将其注册为切面,创建代理对象。
然后,我们需要定义一个切面类。我们将各种增强都实现了一下:
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.CalculatorService.*(..))")
public void before(JoinPoint joinPoint) {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
}
@After("execution(* com.example.CalculatorService.*(..))")
public void after(JoinPoint joinPoint) {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method end");
}
@AfterReturning(pointcut = "execution(* com.example.CalculatorService.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method return " + result);
}
@AfterThrowing(pointcut = "execution(* com.example.CalculatorService.*(..))", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("[ERROR] " + joinPoint.getSignature().getName() + " method throw " + e);
}
@Around("execution(* com.example.CalculatorService.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = null;
try {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
result = joinPoint.proceed();
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method return " + result);
} catch (Throwable e) {
System.out.println("[ERROR] " + joinPoint.getSignature().getName() + " method throw " + e);
throw e;
} finally {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method end");
}
return result;
}
}
这里:
-
@Component
表明这依然是一个 Bean -
@Aspect
表明这是一个切面 -
@Before
、@After
、@AfterReturning
、@AfterThrowing
、@Around
分别表示前置增强、后置增强、返回增强、异常增强和环绕增强这些增强都需要一个切入点表达式来定位连接点。切入点表达式使用的是 SpEL 表达式。我们会在下文中讲解。
然后,CalculatorServiceImpl
中什么也不用做,就能正常输出日志了:
@Service
public class CalculatorServiceImpl implements CalculatorService {
public int add(int a, int b) {
int result = a + b;
return result;
}
public int subtract(int a, int b) {
int result = a - b;
return result;
}
/* ... */
}
这样,我们就实现了 AOP。
对于有多个切面的情况,可以使用 @Order
注解来指定切面的优先级:
@Aspect
@Component
@Order(1)
public class LogAspect {
/* ... */
}
@Aspect
@Component
@Order(2)
public class TransactionAspect {
/* ... */
}
这样,TransactionAspect
的优先级高于 LogAspect
,会先执行 TransactionAspect
。
在上面的例子中,我们使用了切入点表达式来定位连接点。切入点表达式是一个字符串,它有以下几种:
-
execution
:用于匹配方法执行的连接点语法:
execution([访问修饰符] 返回类型 [包名.类名].方法名(参数) [异常])
-
访问修饰符:
public
、protected
、private
、省略 -
返回类型:
*
、具体类型 -
包名:
*
表示一层任意包,..
表示当前包及其子包例如,
*.example
表示某一层包下的example
包,com.example..*
表示com.example
包及其子包下的所有类 -
类名:
*
表示任意类,*Service
表示以Service
结尾的类 -
方法名:
*
表示任意方法,add*
表示以add
开头的方法 -
参数:
(..)
表示任意参数,(*)
表示一个参数,(*, *)
表示两个参数,(*, String)
表示第一个参数任意,第二个参数为String
类型
-
-
within
:用于匹配指定类型内的方法执行连接点,例如within(com.example..*)
表示com.example
包及其子包下的所有类语法:
within([包名.类名])
-
this
:用于匹配当前代理对象类型的方法执行连接点,例如this(com.example.CalculatorService)
表示当前代理对象为CalculatorService
类型的方法执行连接点语法:
this([包名.类名])
-
target
:用于匹配当前目标对象类型的方法执行连接点,例如target(com.example.CalculatorService)
表示当前目标对象为CalculatorService
类型的方法执行连接点语法:
target([包名.类名])
-
args
:用于匹配参数类型的方法执行连接点,例如args(int)
表示参数为int
类型的方法执行连接点语法:
args([参数类型])
-
@annotation
:用于匹配标注了指定注解的方法执行连接点,例如@annotation(com.example.Log)
表示标注了Log
注解的方法执行连接点语法:
@annotation([注解类型])
-
bean
:用于匹配指定 Bean 的方法执行连接点,例如bean(calculatorService)
表示 Bean 名称为calculatorService
的方法执行连接点语法:
bean([Bean 名称])
切入点表达式也可以一次定义,多次使用:
@Pointcut("execution(* com.example.CalculatorService.*(..))")
public void pointcut() {}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
}
这里的 pointcut()
方法被称为切入点方法,它的返回值类型可以是 void
、String
、Object
,也可以没有返回值。
如果要在不同的切面中使用,可以:
@Before("com.example.LogAspect.pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("[INFO] " + joinPoint.getSignature().getName() + " method start, args: " + Arrays.toString(joinPoint.getArgs()));
}
点击展开
如果不适用配置类,也可以在 XML 配置文件中开启 AOP:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.example"/>
</beans>
AOP 同样能够完全基于 XML 配置:
<bean id="logAspect" class="com.example.LogAspect"/>
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut id="pointcut" expression="execution(* com.example.CalculatorService.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
我们来看看 Spring 是如何实现 AOP 的。
在这之前,我们先回顾一下之前在 IoC 的部分提到,AOP 的执行是在属性注入和初始化方法之后、Bean 就绪之前进行的。
代理创建器是 AOP 的核心,它负责创建代理对象。
它也会和其它类一样,一起被注册为 BeanDefinition。然后,Spring 会先注册一个 AutoProxyCreator
,然后再初始化其它 Bean。到了 postProcessAfterInitialization
阶段,代理创建器会扫描所有的 Bean,找到需要代理的 Bean,然后为其创建代理对象。创建后,代理后的 Bean 会被注册到容器中。
我们这里就是扫描 @EnableAspectJAutoProxy
注解,然后注册 AutoProxyCreator
为 BeanDefinition。
点击查看 AutoProxyCreator 源码解读
先来看 @EnableAspectJAutoProxy
注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
// 是否使用 CGLIB 代理(false 表示 JDK 代理)
boolean proxyTargetClass() default false;
// 是否暴露代理对象,即支持目标内部调用
boolean exposeProxy() default false;
}
这里的 @Import
注解表明要导入 AspectJAutoProxyRegistrar
类。我们来看看这个类:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注册 AutoProxyCreator
// 这个类是一个 BeanPostProcessor,负责扫描带有 @Aspect 的 Bean,并为需要代理的 Bean 创建 AOP 代理
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 解析 @EnableAspectJAutoProxy 注解的属性
// 配置 proxyTargetClass 和 exposeProxy
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
这里只是处理了下之前提到的两个属性,proxyTargetClass
和 exposeProxy
。
我们继续跟踪 registerAspectJAnnotationAutoProxyCreatorIfNecessary
方法,最终来到
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果已经注册了
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 判断优先级,如果更高,则替换
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 将代理后的 Bean 注册到容器中
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
// 设置为最高优先级,确保 BeanPostProcessor 优先执行
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
// 设置为基础设施 Bean,不给应用代码暴露
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
总体来讲,就是创建了一个 AutoProxyCreator
的 BeanDefinition,并将其注册到容器中。
在初始化 Bean 之前,会调用 postProcessBeforeInstantiation
方法。
对于 AOP 流程来讲,这时会加载项目中的增强方法。
点击查看 postProcessBeforeInstantiation 源码解读
我们先从 postProcessBeforeInstantiation
方法开始。它有多个实现类,我们这里需要看的是 AbstractAutoProxyCreator
类的实现:
@Override
@Nullable
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
// 结合 Bean 类和名称生成唯一标识
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// 如果已经找到过这个基础类或切面类,则跳过
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// 如果是基础类(Adivce 类等)或者是 @Aspect 注解的切面类,放入 advisedBeans,并跳过(下文会详细讲解)
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// 如果有自定义的 TargetSource(例如池化、懒加载等),赶紧创建代理
// 我们这里不关心这块
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
// 标记为已处理
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
// 获取和当前 Bean 相关的增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
// 创建代理
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
// 将代理类型缓存
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回代理后的 Bean
return proxy;
}
return null;
}
-
isInfrastructureClass(beanClass)
方法:这个方法判断当前 Bean 是否是基础设施类,这里主要判断 Bean 是否是
Advice
、Pointcut
、Advisor
和AopInfrastructureBean
这四个类的子类。protected boolean isInfrastructureClass(Class<?> beanClass) { // 判断当前 Bean 是否是基础设施类,包括 // Advice // Pointcut // Advisor // AopInfrastructureBean boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); if (retVal && logger.isTraceEnabled()) { logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]"); } return retVal; }
-
shouldSkip(beanClass, beanName)
注意,我们不要直接一路跟踪进去。这个函数在
AbstractJAwareAdvisorAutoProxyCreator
中被重写了:@Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // 获取候选的 Advisor 列表(下文会详细讲解) List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { // 检查切面匹配 // 筛选出 @Aspect 注解的切面,并且切面名称和当前 Bean 名称相同 if (advisor instanceof AspectJPointcutAdvisor pointcutAdvisor && pointcutAdvisor.getAspectName().equals(beanName)) { return true; } } // 检查是否以 .ORIGIN 结尾,即已经被代理过的原始类 return super.shouldSkip(beanClass, beanName); }
这个函数主要是检查当前 Bean 是否是 @Aspect 注解的切面
我们跟踪进
findCandidateAdvisors()
方法看一下。注意,我们先进入其覆写方法:@Override protected List<Advisor> findCandidateAdvisors() { // 根据缓存的 Advisor Bean 名称来查找所有的 Advisor(下文会详细讲解) List<Advisor> advisors = super.findCandidateAdvisors(); if (this.aspectJAdvisorsBuilder != null) { // 扫描容器中所有 @Aspect 注解的 Bean,并生成对应的 Advisor(下文会详细讲解) advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
挨个来看:
-
super.findCandidateAdvisors()
这个方法会根据缓存的 Advisor Bean 名称来查找所有的 Advisor:
public List<Advisor> findAdvisorBeans() { // 获取所有的 Advisor Bean 名称 String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // 查找 Bean 名称 advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<>(); } // 遍历并筛选出符合条件的 Advisor List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { // 过滤符合条件的 Advisor(可能会指定前缀) if (isEligibleBean(name)) { // 跳过当前正在创建的 Advisor if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { // 添加到候选列表 advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException bce) { String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { if (logger.isTraceEnabled()) { logger.trace("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } continue; } } throw ex; } } } } return advisors; }
-
buildAspectJAdvisors()
这个方法会扫描容器中所有
@Aspect
注解的 Bean,并生成对应的 Advisor:public List<Advisor> buildAspectJAdvisors() { // 缓存优化,如果首次调用则没用 List<String> aspectNames = this.aspectBeanNames; // 没有缓存的情况 if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { // 获取所有 Bean 名称 List<Advisor> advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false); // 遍历并筛选出符合条件的 Bean for (String beanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } // 获取 Bean 的类型并判断是否被 @Aspect 注解标记 Class<?> beanType = this.beanFactory.getType(beanName, false); if (beanType == null) { continue; } if (this.advisorFactory.isAspect(beanType)) { try { // 解析 @Aspect 注解 AspectMetadata amd = new AspectMetadata(beanType, beanName); // 处理单例 Aspect if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 创建 Aspect 实例工厂 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 解析通知方法并生成 Advisor(下文会详细讲解) List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); // 处理缓存 if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } // 处理非单例 Aspect else { if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } aspectNames.add(beanName); } catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) { if (logger.isDebugEnabled()) { logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex); } } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } // 已经有缓存的情况,直接从缓存中获取 List<Advisor> advisors = new ArrayList<>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }
跟踪进核心的
getAdvisors()
方法。它负责将 Aspect 类中的通知方法(如@Before
、@Around
)和字段声明(如@DeclareParents
)转换为 Advisor 对象,并处理懒加载切面的实例化逻辑@Override public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 获取 Aspect 类和名称 Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); // 懒加载切面实例工厂 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); // 返回切面类中的所有通知方法(下文会详细讲解) List<Advisor> advisors = new ArrayList<>(); for (Method method : getAdvisorMethods(aspectClass)) { if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) { // 转换为 Advisor 对象(下文会详细讲解) Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { advisors.add(advisor); } } } // 处理懒加载 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 处理 @DeclareParents 注解 for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
-
getAdvisorMethods(aspectClass)
这个方法会获取 Aspect 类中的所有通知方法
private List<Method> getAdvisorMethods(Class<?> aspectClass) { List<Method> methods = new ArrayList<>(); ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter); if (methods.size() > 1) { methods.sort(adviceMethodComparator); } return methods; }
-
getAdvisor
这个方法用于将 Aspect 类中的通知方法(如
@Before
、@Around
)转换为 Advisor 对象@Override @Nullable public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 解析切点表达式 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } try { // 实例化 Advisor 对象 // 包含了切点、通知方法、切面实例工厂、声明顺序和切面名称 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } catch (IllegalArgumentException | IllegalStateException ex) { if (logger.isDebugEnabled()) { logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex); } return null; } }
最后实例化 Advisor 对象一步我懒得贴代码了,其实就是设置了几个属性,然后将其实例化。
-
还记得之前在看哪个函数吗?
shouldSkip
!现在,我们明白了,这个函数其实是将@Aspect
注解的切面类给实例化成 Advisor 了,并返回是不是传入的 Bean 是不是已经处理过的 Advisor。 -
我们再回溯!postProcessBeforeInstantiation
方法主要就是做了 shouldSkip
的方法,剩下的东西都无关紧要了。
在实例化过 Bean 并注入属性后,我们已经找出了所有的 Advisor。接下来,所有 Bean 将会依次初始化,然后我们要为这些 Advisor 创建代理对象。
点击查看 postProcessAfterInitialization 源码解读
我们要看的是 AbstractAutoProxyCreator
类的 postProcessAfterInitialization
方法:
@Override
@Nullable
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 生成唯一标识
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyBeanReferences.remove(cacheKey) != bean) {
// 包装 Bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这个函数看不出什么。往下看 wrapIfNecessary
方法:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经被显示代理过了,则直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 如果已经增强过了,则直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 又来了,同上,找出 @Aspect 注解的切面类
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取适用的增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
很明显,这个函数主要是做了两件事:
- 获取这个 Bean 需要使用哪些增强器
- 用这些增强器创建代理
接下来,我们分别看一下这两个方法。
-
getAdvicesAndAdvisorsForBean()
方法:@Override @Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
跟踪进
findEligibleAdvisors()
方法:protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 先找到所有的候选 Advisor,这个函数我们讲过了 List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 筛选出符合条件的 Advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { try { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } catch (BeanCreationException ex) { throw new AopConfigException("Advisor sorting failed with unexpected bean creation, probably due " + "to custom use of the Ordered interface. Consider using the @Order annotation instead.", ex); } } return eligibleAdvisors; }
这个函数就是找到所有的候选 Advisor,然后筛选出符合条件的 Advisor。
我们一路跟踪
findAdvisorsThatCanApply()
方法:public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } // 单独处理 IntroductionAdvisor,因为它可能为目标类引入新接口,影响后续 Advisor 的筛选 List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } // 处理其他 Advisor(下文会详细讲解) boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { continue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
显然,这里的重点就是
canApply()
方法。我们来看看这个方法:public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { // 对于 IntroductionAdvisor,检查是否匹配类过滤器 if (advisor instanceof IntroductionAdvisor ia) { return ia.getClassFilter().matches(targetClass); } // 对于 PointcutAdvisor,检查是否匹配切点 else if (advisor instanceof PointcutAdvisor pca) { return canApply(pca.getPointcut(), targetClass, hasIntroductions); } // 对于其他类型的 Advisor,直接返回 true else { return true; } }
跟踪进下一个
canApply()
方法:public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); // 首先检查 Pointcut 的类过滤器是否匹配目标类 if (!pc.getClassFilter().matches(targetClass)) { return false; } // 如果匹配所有方法,则直接返回 true MethodMatcher methodMatcher = pc.getMethodMatcher(); if (methodMatcher == MethodMatcher.TRUE) { return true; } // 针对 IntroductionAwareMethodMatcher,检查是否匹配目标类的方法 IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher iamm) { introductionAwareMethodMatcher = iamm; } // 收集待检查的类和接口 Set<Class<?>> classes = new LinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); // 遍历所有类和接口,检查方法匹配 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
这里往下看越看越深,懒得看了。总之,这个函数就是在判断当前 Bean 需要使用哪些增强器。
-
createProxy
方法:private Object buildProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) { if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) { AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass); } // 初始化 ProxyFactory,并从当前对象复制代理配置 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // 如果使用 CGLIB 代理 if (proxyFactory.isProxyTargetClass()) { // 如果是代理类或者 Lambda,则直接添加接口 if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) { for (Class<?> ifc : beanClass.getInterfaces()) { proxyFactory.addInterface(ifc); } } } // 如果是 JDK 代理 else { // 找到合适的接口 if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } // 收集所有的拦截器 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); // 配置目标源和钩子方法 proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); // 冻结代理配置 proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 获取类加载器 ClassLoader classLoader = getProxyClassLoader(); if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) { classLoader = smartClassLoader.getOriginalClassLoader(); } // 生成代理工厂 return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader)); }
这个函数主要将拦截器添加到
ProxyFactory
中,然后创建代理类。-
buildAdvisors()
方法:protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) { // 通用拦截器 Advisor[] commonInterceptors = resolveInterceptorNames(); // 所有拦截器 List<Object> allInterceptors = new ArrayList<>(); if (specificInterceptors != null) { if (specificInterceptors.length > 0) { allInterceptors.addAll(Arrays.asList(specificInterceptors)); } // 默认情况下特定拦截器先执行、通用拦截器后执行 if (commonInterceptors.length > 0) { if (this.applyCommonInterceptorsFirst) { allInterceptors.addAll(0, Arrays.asList(commonInterceptors)); } else { allInterceptors.addAll(Arrays.asList(commonInterceptors)); } } } if (logger.isTraceEnabled()) { int nrOfCommonInterceptors = commonInterceptors.length; int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0); logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors + " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors"); } // 将所有拦截器转换为 Advisor Advisor[] advisors = new Advisor[allInterceptors.size()]; for (int i = 0; i < allInterceptors.size(); i++) { advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i)); } return advisors; }
这个函数干了两件事:
-
处理了通用拦截器和特定拦截器的顺序问题
- 通用拦截器是例如
@Bean
注解的拦截器 - 特定拦截器是例如
@Aspect
注解定义切面,然后在@Pointcut
注解中定义的拦截器
- 通用拦截器是例如
-
将所有拦截器转换为 Advisor
例如,
@Before
注解原来是Advice
,现在变成了DefaultPointcutAdvisor
-
-
getProxy()
和getProxyClass()
方法:这两个方法一层又一层的嵌套调用,当然都没干什么实事。我们直接看最终调用的
DefaultAopProxyFactory
类的createAopProxy
方法:@Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 判断是否满足使用 CGLIB 代理的条件 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 如果是接口 / 代理类 / Lambda 表达式,则回退到 JDK 代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 使用 CGLIB 代理 return new ObjenesisCglibAopProxy(config); } else { // 使用 JDK 代理 return new JdkDynamicAopProxy(config); } }
这个函数区分了 CGLIB 代理和 JDK 代理的情况,并分别进行了处理。
-
至此,全部分析完。CGLIB 代理和 JDK 代理具体怎么实现的就不讲了,感兴趣的可以去看 CglibAopProxy
和 JdkDynamicAopProxy
这两个方法。
我们上一篇在谈 Bean 生命周期的时候,对 AOP 还未如此深入的了解。现在我们可以把 AOP 的流程合并进 Bean 的生命周期了:
-
BeanDefinition 注册(不属于 Bean 生命周期)
通过
@ComponentScan
扫描到的 Bean 会被注册到容器中执行
ApplicationContextAware
和LoadTimeWeaverAware
AOP:创建
AnnotationAwareAspectJAutoProxyCreator
的 BeanDefinition,并注册到容器中 -
实例化
根据包扫描的结果,通过构造函数或工厂方法创建 Bean 实例
为解决循环依赖,Spring 可能提前暴露未初始化的 Bean
-
依赖注入
根据
@Value
、@Autowired
、@Resource
等注解注入属性值和依赖如果存在循环依赖,从三级缓存获取提前暴露的 Bean 引用
-
Aware 接口回调
执行 Aware 接口的回调方法
按顺序执行
BeanNameAware
$$\to$$ BeanClassLoaderAware
$$\to$$ BeanFactoryAware
-
BeanPostProcess 前置处理
执行
BeanPostProcessor
的postProcessBeforeInitialization
方法AOP:扫描带有
@Aspect
注解的 Bean,创建 Advisor -
初始化方法
依次处理
-
@PostConstruct
注解的方法 -
InitializingBean
接口的afterPropertiesSet()
方法 - 自定义的初始化方法
-
-
BeanPostProcess 后置处理
执行
BeanPostProcessor
的postProcessAfterInitialization
方法AOP:为 Bean 创建代理对象
-
Bean 就绪
所有 Bean 都放入一级缓存,以供使用
-
销毁
依次处理
-
@PreDestroy
注解的方法 -
DisposableBean
接口的destroy()
方法 - 自定义的销毁方法
-
很多人讲,AOP 是在初始化后才处理的。现在,我们看到这种说法并不严谨。尽管在初始化后才创建代理对象,但在 BeanPostProcess 前置处理的时候,已经开始扫描所有的切面了并且创建了 Advisor。
更进一步说,在进入 Bean 生命周期之前,Spring 就已经根据 @EnableAspectJAutoProxy
注解创建了 AnnotationAwareAspectJAutoProxyCreator
的 BeanDefinition,并注册到容器中了。