背景
按照软件重构的思想,当多个类中存在相同的代码时,需要提取公共部分来消除代码坏味道。Java的继承机制允许用户在纵向上通过提取公共方法或者公共部分(模版方法方式)至父类中以消除代码重复问题;日志、访问控制、性能监测等重复的非业务代码揉杂在业务代码之中无法横向抽取,AOP技术为其提供了一个解决方案。
AOP技术将这些重复的非业务代码抽取出为一个模块,通过技术整合还原代码的逻辑和功能;即:在代码层面上消除了重复度,提高了可维护性,并在功能层面上得到还原。抽取重复代码作为一个模块是用户的问题,然而技术整合(对目标织入增强逻辑,后文介绍)以实现功能还原是AOP的目标和工作重心,Spring AOP是该技术的一种实现。
本文作为Spring系列的第八篇,介绍Spring框架中AOP的使用、注意事项和实现原理,原理部分会结合Spring框架源码进行。
Spring系列的后续文章如Spring系列-9 Async注解使用与原理和Spring系列-10 事务机制其底层原理都是Spring AOP。
1.AOP
常见的AOP实现方案有Spring AOP和AspectJ:相对于Spring AOP而言,AspectJ是一种更成熟、专业的AOP实现方案。AOP的技术整合(织入增强逻辑)可以发生在编译器、类加载期以及运行期:AspectJ在编译器(ajc)和类加载器(使用特定的类加载器)实现;Spring AOP在运行时通过动态代理方式实现。AspectJ提供了完整了AOP方案,而Spring AOP从实用性出发未常见的应用场景提供了技术方案,如不支持静态方法、构造方法等的AOP。
本文考虑到文章篇幅,下文暂不涉及其他AOP技术,后续可能单独整理一篇《AspectJ使用与原理》
Spring AOP构建于IOC之上,与IOC一起作为Spring框架的基石。Spring AOP底层使用动态代理技术实现,包括:JDK动态代理与CGLIB动态代理;JDK动态代理技术要求被代理对象基于接口,而CGLIB动态代理基于类的继承实现代理,从而要求被代理类不能为final类且被代理的方法不能被final、staic、private等修饰。二者都有局限性,在一定程度上相互弥补。
1.1 基本概念
[1] 执行点:在Spring AOP中指代目标类中具体的方法;
[2] 连接点:包含位置信息的执行点,位置信息包括:方法执行前、后、前后、异常抛出等;
[3] 切点:根据指定条件(类是否符合、方法是否符合等)过滤出的执行点的集合;
[4] 通知/增强:为目标对象增加的新功能,如在业务代码中引入日志、访问控制等功能;
[5] 切面:切面由切点和通知组成;
[6] 织入:将切面织入目标对象,形成代理对象的过程。
1.2 增强类型
Spring中使用Advise标记接口表示增强,Spring根据方位信息(方法执行前后、环绕、异常抛出等)为其定义了不同的子类接口。
public interface Advice {}
1.2.1 增强类型相关接口
[1] 前置增强
BeforeAdvice接口表示前置增强,由于Spring当前仅支持方法增强,所以可用的接口为MethodBeforeAdvice.
//同Advise接口,BeforeAdvice也是个空接口
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
如上所示,MethodBeforeAdvice接口中仅有一个before
方法,入参分别是方法对象、参数数组、目标对象;该方法会在目标对象的方法调用前调用。
[2] 后置增强
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}
该方法中仅有一个afterReturning
方法,入参比before
多处一个返回值;该方法会在目标对象的方法调用后调用。
[3] 环绕增强
@FunctionalInterface
// Interceptor 是Advise的字接口,且是空接口
public interface MethodInterceptor extends Interceptor {
@Nullable
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}
可通过invocation.proceed()
语句调用目标对象方法并获得放回值,可在前后自定义逻辑,相对于前置和后置有更高的灵活性。
[4] 异常抛出增强
public interface ThrowsAdvice extends AfterAdvice {
}
ThrowsAdvice是一个空接口,起标签作用。在运行期间Spring通过反射调用afterThrowing
接口,该接口可以被定义为:void afterThrowing(Method method, Object[] args, Object target, Throwable exception);
其中method、args和target是可选的,exception参数是必选的;在目标方法抛出异常后,实施增强。
除此之外,框架还定义了一种引介增强,用于在目标类中添加一些新的方法和属性。
1.2.2 案例介绍
case 1:前置、后置、环绕增强
定义前置通知:
@Slf4j
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
LOGGER.info("----before----");
}
}
定义后置通知:
@Slf4j
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
LOGGER.info("----after----");
}
}
定义环绕通知:
@Slf4j
public class MyRoundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
LOGGER.info("====round before====");
Object result = invocation.proceed();
LOGGER.info("====round after====");
return result;
}
}
测试用例如下:
public class AdviceAopTest {
@Test
public void testAdvice() {
ProxyFactory proxyFactory = new ProxyFactory();
TaskService taskService = new TaskServiceImpl();
proxyFactory.setTarget(taskService);
proxyFactory.setInterfaces(TaskService.class);
// 添加前置增强
proxyFactory.addAdvice(new MyBeforeAdvice());
// 添加后置增强
proxyFactory.addAdvice(new MyAfterReturningAdvice());
// 添加环绕增强
proxyFactory.addAdvice(new MyRoundAdvice());
// 获取代理对象
TaskService proxy = (TaskService)proxyFactory.getProxy();
proxy.sync();
}
}
运行结果如下所示:
case 2:异常抛出增强
修改目标类代码逻辑:
@Slf4j
public class TaskServiceImpl implements TaskService{
@Override
@SneakyThrows
public void sync() {
LOGGER.info("[sync data]");
throw new Exception("");
}
}
测试用例如下:
public class ThrowsAdviceTest {
@Test
public void testAdvice() {
ProxyFactory proxyFactory = new ProxyFactory();
TaskService taskService = new TaskServiceImpl();
proxyFactory.setTarget(taskService);
proxyFactory.setInterfaces(TaskService.class);
proxyFactory.addAdvice(new MyThrowsAdvice());
TaskService proxy = (TaskService)proxyFactory.getProxy();
proxy.sync();
}
}
结果如下:
1.3 切点类型
框架定义切点是为了从目标类的连接点(执行点)中过滤出符合条件的部分,为此在切点类的内部提供类两个过滤器:ClassFilter和MethodMatcher,分别对类型和方法进行过滤。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
// Pointcut.TRUE 对象表示所有目标类的所有方法均满足条件
// (实例对应的ClassFilter和MethodMatcher对象的match方法均返回true)
Pointcut TRUE = TruePointcut.INSTANCE;
}
Pointcut切点接口定义如上所示,Spring并基于此扩展出了多种切点类型;使得可以根据方法名、参数、是否包含注解以及表达式等进行过滤。
1.4 切面类型
Spring使用Advisor表示切面类型,可以分为3类:一般切面Advisor、切点切面PointcutAdvisor、引介切面IntroductionAdvisor;一般切面Advisor仅包含一个Advice, 即表示作用对象是所有目标类的所有方法;PointcutAdvisor包含Advice和Pointcut信息,可以通过切点定位出满足Pointcut过滤条件的执行点集合;IntroductionAdvisor对应于引介切点和增强。
其中:PointcutAdvisor及其子类DefaultPointcutAdvisor是较为常见的切面类型,源码如下:
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
private Pointcut pointcut = Pointcut.TRUE;
private Advice advice = EMPTY_ADVICE;
public DefaultPointcutAdvisor() {
}
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
}
DefaultPointcutAdvisor包含一个切点和一个增强类型属性:Pointcut的默认值为Pointcut.TRUE表示所有目标类的所有方法均为连接点;Advice的默认值为EMPTY_ADVICE:Advice EMPTY_ADVICE = new Advice() {};
, 即表示不进行增强。
章节-1.2测试用例中为ProxyFactory添加切面部分逻辑为:proxyFactory.addAdvice(new MyBeforeAdvice());
等价于 proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new MyBeforeAdvice()));
.
2.使用方式
章节-1中涉及的ProxyFactory代理工厂提供了基于切面构造代理对象的能力,Spring框架结合IOC对此进行了一层封装以适应多种场景。封装后为用户提供了一套Spring风格的“API”(使用方式),该部分是本章节的重点部分。
2.1 xml配置方式
引入AOP的schema:
<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-4.3.xsd">
方式1:使用aop:aspect标签
定义目标类:
@Slf4j
public class ApplicationRunner {
public void run() {
LOGGER.info("exec...");
}
}
引入增强类:
@Slf4j
public class EnhanceLog {
public void beforeExec() {
LOGGER.info("before exec");
}
public void afterExec() {
LOGGER.info("after exec");
}
}
在xml文件中配置AOP:
<aop:config>
<aop:pointcut id="runnerExecPc" expression="execution(* *.run(..))"/>
<aop:aspect ref="enhanceLog">
<aop:before method="beforeExec" pointcut-ref="runnerExecPc"/>
<aop:after method="afterExec" pointcut-ref="runnerExecPc"/>
</aop:aspect>
</aop:config>
方式2:使用aop:advisor标签
引入增强类LogAdvice,需要实现org.aopalliance.intercept.MethodInterceptor接口:
@Slf4j
public class LogAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
LOGGER.info("before intercept exec");
methodInvocation.proceed();
LOGGER.info("after intercept exec");
return null;
}
}
在xml文件中配置AOP:
<aop:config>
<aop:pointcut id="runnerExecPc" expression="execution(* *.run(..))"/>
<aop:advisor advice-ref="logAdvice" pointcut-ref="runnerExecPc"/>
</aop:config>
其中:advisor将所有的逻辑都封装在了MethodInterceptor的invoke方法中,通过方法完成增强;aspect通过配置对外展示需要增强逻辑,而不需要实现MethodInterceptor等Advice系列接口。相对而言,aspect的代码侵入性较低。
2.2 注解方式
Spring通过整合AspectJ为AOP提供了注解形式的使用方式;因此使用注解时,需要添加对aspectjweaver
的依赖(由org.aspectj
提供)。
2.2.1 切面注解 @Aspect
注解在类上用于标记切面类;其他注解都可以添加在该类中的方法上。
2.2.2 增强注解 @Before @After @Around @AfterThrowing @AfterReturing
被增强注解的方法内容作为增强。@Before表示前置增强,@AfterReturing表示后置增强,@Around表示环绕增强,@AfterThrowing表示异常抛出增强,@After表示方法正常执行完或者异常抛出都会执行的增强逻辑;与Spring中定义的增强类型基本保持一致。
被上述注解标注的方法可以增加一个JoinPoint类型(Around为ProceedingJoinPoint类型)的参数(也可不加)用于获取上下文信息;另外,@AfterThrowing还可添加异常类型的参数,而@AfterReturing可以添加一个Object类型的参数(表示运行结果),以下通过案例的形式进行介绍。
添加配置类:
@Configuration
@ComponentScan(basePackages = "com.seong.demo.annotation")
@EnableAspectJAutoProxy
public class AopDemoConfiguration {
}
添加目标类:
@Component
@Slf4j
public class DataTask {
public String syncData() {
LOGGER.info("start sync data");
return "success";
}
}
添加测试用例:
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopDemoConfiguration.class);
DataTask dataTask = (DataTask)context.getBean("dataTask");
dataTask.syncData();
}
}
case 1: 前置、后置
切面配置如下所示:
@Component
@Aspect
@Slf4j
public class MyNormalAspect {
@Before("execution(public String com.seong.demo.annotation.DataTask.*(..))")
public void beforeExec(JoinPoint joinPoint) {
LOGGER.info("[Before DataTask], joinPoint is {}.", joinPoint);
}
@AfterReturning("execution(public String com.seong.demo.annotation.DataTask.*(..))")
public void afterExec(JoinPoint joinPoint) {
LOGGER.info("[After DataTask], result is {}, joinPoint is {}.", joinPoint);
}
}
得到如下运行结果:
case 2: 环绕通知
在切面中配置环绕增强,如下所示:
@Component
@Aspect
@Slf4j
public class MyAroundAspect {
@SneakyThrows
@Around("execution(public String com.seong.demo.annotation.DataTask.*(..))")
public void aroundExec(ProceedingJoinPoint joinPoint) {
LOGGER.info("[Around DataTask] call before.");
Object result = joinPoint.proceed();
LOGGER.info("[Around DataTask] call end, result is {}.", result);
}
}
得到如下运行结果:
case 3: 异常抛出增强
在切面中配置异常抛出增强,如下所示:
@Component
@Aspect
@Slf4j
public class MyExceptionAspect {
@AfterThrowing("execution(public String com.seong.demo.annotation.DataTask.*(..))")
public void afterThrowingExec(JoinPoint joinPoint) {
LOGGER.info("[AfterThrow DataTask], joinPoint is {}.", joinPoint);
}
}
得到如下运行结果:
2.4 expression表达式
参考: AspectJ官网 或《AspectJ使用与原理》
3.实现原理
在介绍原理之前,读者可以自测一下:如果可以清晰地理清循环依赖场景下的AOP代理流程,可以跳过本章的阅读;否则,建议阅读完本章节后再次自测一下。
3.1 EnableAspectJAutoProxy注解
@EnableAspectJAutoProxy可注解在启动类或者配置类上,开启AOP功能;该注解的核心作用是向容器中导入AnnotationAwareAspectJAutoProxyCreator并对其进行配置。
3.1.1 EnableAspectJAutoProxy注解
源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
EnableAspectJAutoProxy的定义中存在两个布尔类型的属性,这两个属性最后会传递给AnnotationAwareAspectJAutoProxyCreator:
[1] proxyTargetClass表示是否强制指定使用CGLIB代理,默认值为false;当指定为true时,AOP使用CGLIB代理(包含绝大部分场景,对业务代码而言成立)。可参考 Spring系列-9 Async注解使用与原理:文章最后对AOP使用何种代理类型进行了说明。
[2] exposeProxy表示是否暴露代理对象,默认值为false; 当设置为true时,在目标对象内部通过AopContext.currentProxy()
可以获取代理对象,可用于解决同一个类中方法相互调用导致代理失效问题,当然也可以用于解决此种场景下的事务失效问题,即事务-1 事务隔离级别和Spring事务传播机制文中章节-3.3涉及的注意事项。
此处需要注意:当exposeProxy设置为false时或使用AspectJ实现代理时,AopContext.currentProxy()
会抛出异常。
除此之外,EnableAspectJAutoProxy注解中还声明了@Import(AspectJAutoProxyRegistrar.class)
,AnnotationAwareAspectJAutoProxyCreator就是通过该Import导入至IOC容器中。
3.1.2 AspectJAutoProxyRegistrar
通过@Import导入的AspectJAutoProxyRegistrar是一个ImportBeanDefinitionRegistrar类型,代码如下:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
逻辑较为简单:
[1] 通过AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
向IOC容器中添加AnnotationAwareAspectJAutoProxyCreator的Beandefinition;
[2] 获取注解EnableAspectJAutoProxy的定义信息,如果不为空,取出"proxyTargetClass"和"exposeProxy"信息,对AnnotationAwareAspectJAutoProxyCreator的Beandefinition对应属性进行设置,从而实现配置的传递。
这里有个细节可以看一下,跟踪AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
的调用链:
String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";
// ⚠️调用步骤-1
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
// ⚠️调用步骤-2
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
// ⚠️调用步骤-3
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
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;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
跟随调用链进入调用步骤-3时,如参cls为AnnotationAwareAspectJAutoProxyCreator.class字节码对象,
registry为IOC容器,source为null. 主线逻辑时将AnnotationAwareAspectJAutoProxyCreator.class信息封装成RootBeanDefinition(同时设置优先级和基础设置角色信息),并制定beanName为"org.springframework.aop.config.internalAutoProxyCreator"
,然后添加到IOC容器仓储中。
方法中存在一个if判断,当容器仓储中存在beanName为"org.springframework.aop.config.internalAutoProxyCreator"的BeanDefinition时,根据优先级进行替换,即注入更高优先级的Bean。优先级的定义在AopConfigUtils类中:
private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);
static {
// Set up the escalation list...
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
如上述代码:按照定义顺序确定的优先级:AnnotationAwareAspectJAutoProxyCreator>AspectJAwareAdvisorAutoProxyCreator>InfrastructureAdvisorAutoProxyCreator.
至此,通过@EnableAspectJAutoProxy注解向IOC仓储中引入了AnnotationAwareAspectJAutoProxyCreator的BeanDefinition。
说明:当容器收集BeanPostProcessor类型的Bean对象时,会自动解析该BeanDefinition、实例化对象、属性设置并注入到IOC容器中,该部分逻辑可以参考Spring系列-2 Bean的生命周期,区别在于AnnotationAwareAspectJAutoProxyCreator的注入流程发生在Spring容器收集BeanPostProcessor类型的Bean对象时,而不是加载所有的非懒加载单例Bean。
3.2 AnnotationAwareAspectJAutoProxyCreator
因场景多而抽象、细节比较复杂,本文考虑先整体梳理后细节分析。 本章节 中对多场景下AOP流程进行的整体说明,细节部分抽出来在 章节3.3 中进行介绍。
AnnotationAwareAspectJAutoProxyCreator间接实现了SmartInstantiationAwareBeanPostProcessor接口,因此也实现了InstantiationAwareBeanPostProcessor和BeanPostProcessor接口。
即实现了如下方法:
// SmartInstantiationAwareBeanPostProcessor接口中定义:
Object getEarlyBeanReference(Object bean, String beanName);
// InstantiationAwareBeanPostProcessor接口中定义:
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName);
// BeanPostProcessor接口中定义:
Object postProcessAfterInitialization(Object bean, String beanName);
上述三个方法是Spring AOP功能实现的基础,也请读者重点关注。
这部分要求读者预先对Bean的生命周期、三级缓存和循环依赖等概念和流程比较清晰,可参考Spring系列的相关文章。以下对AOP流程分场景进行介绍,过程中会涉及上述三个方法。
3.2.1 普通Bean的AOP场景
如Spring系列-2 Bean的生命周期中对Bean生命周期的描述,在Bean对象的初始化后期会调用Object postProcessAfterInitialization(Object bean, String beanName);
接口;
进入AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization
方法:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
逻辑较为简单:
1)根据bean的name和class类型获取cacheKey(读者可忽略细节,一般情况下直接认为等价于beanName,框架为了照顾不同场景做的一层封装);
2)判断this.earlyProxyReferences集合中是否包含该beanName,this.earlyProxyReferences存放被提前暴露的Bean对象(循环依赖场景使用到);本场景下this.earlyProxyReferences不包含beanName,因此进入return wrapIfNecessary(bean, beanName, cacheKey);
逻辑;
3)wrapIfNecessary方法的核心逻辑是判断该Bean对象是否需要AOP, 如果需要返回代理后的对象,否则直接返回该对象,内容在 章节3.3 中进行。
3.2.2 自定义Bean对象场景
自定义Bean对象场景指用户通过InstantiationAwareBeanPostProcessor在postProcessBeforeInstantiation
方法中定义了Bean对象,此时Bean的生命周期会直接进入BeanPostProcessor(此时为AnnotationAwareAspectJAutoProxyCreator)的postProcessAfterInitialization
方法中,代理流程同 3.2.1 普通Bean的AOP场景。
3.2.3 循环依赖场景
首先交代一下结果:IOC容器完成刷新后,相互依赖的Bean对象依赖的是AOP后的代理对象,且代理对象中持有原始对象的引用。
循环依赖场景相对较为复杂,本章节结合案例进行介绍,案例同如下:
ComponentA和ComponentB对象相互依赖, 且ComponentA需要被AOP;当ComponentA先被初始化时,流程图如下所示:
上述流程与Spring系列-4 循环依赖与三级缓存主体流程完全相同,因此代码介绍时细节部分不再赘述,默认认为读者已经阅读过该文章或者对循环依赖与三级缓存概念和流程比较清楚。
【1】ComponentA对象实例化:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// ⚠️step-1.实例化Bean对象
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
// ...
}
【2】ComponentA在完成Bean实例化后,如果IOC支持循环依赖,则将获取ComponentA对象的lambda表达式存入三级缓存
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// ...
// ⚠️step-2.将bean对象存入三级缓存
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// ...
}
注意,这里有个前提条件this.allowCircularReferences=true;
;表示只有IOC容器支持循环依赖才会将该对象保存在三级缓存中。
在Spring项目中,该变量的定义如下:
private boolean allowCircularReferences = true;
即不进行特殊设置时,默认支持循环依赖。
在SpringBoot项目中,项目启动时会进行属性的设置
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
而定义如下:
private Boolean allowCircularReferences;
即不进行特殊设置时,默认为false,不支持循环依赖。
() -> getEarlyBeanReference(beanName, mbd, bean)
该表达式在后文调用时再进行介绍。
【3】bean对象的初始化逻辑-包括属性注入和AOP代理
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// ...
// ⚠️step-3.bean对象的初始化逻辑-包括属性注入和AOP代理
Object exposedObject = initializeBean(beanName, exposedObject, mbd);
// ...
}
在Bean对象的初始化流程中会依次完成属性的依赖注入(AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor) 和 AOP流程(AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor).
【3-1】ComponentA开始执行属性注入
在依赖注入阶段,首先尝试通过getBean方法
从IOC中获取依赖的ComponentB对象,获取不到则进行创建:
// getBean中通过getSingleton方法根据beanName从IOC容器中查找Bean对象
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
getSingleton(beanName, true);
逻辑较为简单:依次从Spring的三级缓存中获取Bean对象,否则返回null对象。
此时由于Spring容器未出实话ComponentB对象,getSingleton(String beanName)
返回null, 从而触发创建ComponentB Bean对象的流程。
【3-2】ComponentB开始执行属性注入
ComponentB在完成实例化和属性设置后,进行属性的依赖注入阶段,首先尝试通过getBean方法
从IOC中获取依赖的ComponentB对象,获取不到则进行创建。此时,由于第三级缓存中存放了ComponentA对象相关的lambda表达式,会在执行getSingleton方法时进入以下逻辑:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// ⚠️1.调用lambda表达式,从三级缓存中获取Bean对象
singletonObject = singletonFactory.getObject();
// ⚠️2.将Bean对象存放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// ⚠️3.删除三级缓存中对应的记录
this.singletonFactories.remove(beanName);
}
}
从三级缓存中取出ComponentA对象,并将其保存在二级缓存中,同时删除三级缓存中对应的记录。
【3-3】通过三级缓存索取ComponentA对象:执行lambda表达式
三级缓存的singletonFactory.getObject();
调用了() -> getEarlyBeanReference(beanName, mbd, bean)
,进入getEarlyBeanReference方法:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
核心逻辑是调用getEarlyBeanReference
方法获取Bean对象;
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
getEarlyBeanReference
方法逻辑极其简明:将"componentA"和ComponentA的原始Bean对象
作为键值对存入缓存中,然后调用wrapIfNecessary方法返回代理后的ComponentA对象(以下问了理解和表述方便,使用ComponentA-agent Bean对象
表示)。
【3-4】ComponentB拿到代理对象后开始执行属性注入
在从IOC中拿到ComponentA-agent Bean对象
后进行ComponentB的依赖注入,之后将ComponentB加入IOC容器中;
并将ComponentB对象返回。
【3-5】ComponentA 完成属性注入
在从IOC中拿到ComponentB的Bean对象
后进行ComponentA的依赖注入,完成依赖注入后,进入ComponentA的AOP流程。
【3-6】ComponentA的AOP流程
进入AbstractAutoProxyCreator的postProcessAfterInitialization
方法:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
注意:此时参数中的bean对象是原始ComponentA对象。
this.earlyProxyReferences中储存了"componentA"和ComponentA的原始Bean对象
作为键值,因此this.earlyProxyReferences.remove(cacheKey) != bean
将返回false, 即直接将原始ComponentA对象返回,不再做处理。
【4】逻辑判断—保证返回的是代理后的Bean对象
在执行逻辑判断前,先梳理一下现状:
1.Spring的第二级缓存中包含了ComponentA-agent Bean对象
2.bean和exposedObject指向原始Bean对象
继续进入ComponentA对象Bean生命周期的doCreateBean方法:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// ...
// ⚠️step-4.逻辑判断—保证返回的是代理后的Bean对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
}
return exposedObject;
}
通过getSingleton(beanName, false)
将取出存放在二级缓存中的ComponentA-agent Bean对象
;
由于exposedObject和exposedObject指向原始Bean对象,因此需要将ComponentA-agent Bean对象
赋值给exposedObject对象并返回。
然后进入addSingleton代码逻辑:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
将ComponentA-agent Bean对象
存入以及缓存,同时删除二级缓存中的相关记录。
3.3 AnnotationAwareAspectJAutoProxyCreator.wrapIfNecessary
在介绍wrapIfNecessary方法之前,先介绍或回顾一下需要关注的属性:
private final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<>(16);
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);
targetSourcedBeans:存放自定义targetSource对象的beanName集合;
earlyProxyReferences:存放beanName和提前暴露的代理对象的映射关系;
proxyTypes:存放beanName和代理对象类型的映射关系;
advisedBeans:存放beanName和目标Bean对象是否需要被代理的映射关系;advisedBeans起缓存作用:对于不需要被代理的目标Bean对象进行标记(如AOP内部定义的类型),从而不需要反复判断。
为突出主线逻辑,以下代码介绍时删除advisedBeans缓存(查询优化)和targetSourcedBeans(几乎用不到的自定义扩展)相关逻辑:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// ⚠️1.如果是基础设施类或者满足shouldSkip规则的,不需要代理;
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
return bean;
}
// 2.获取Bean对象相关的Advisor列表
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// ⚠️3.如果Advisor列表为空(增强逻辑为空),则不需要进行代理;否则根据specificInterceptors为bean创建代理对象
if (specificInterceptors != null) {
return createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
} else {
return bean;
}
}
【1】判断是否跳过代理流程:
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
return bean;
}
对Advice、Pointcut、AopInfrastructureBean、Advisor、Aspect、AspectJPointcutAdvisor等类型以及使用@Aspect注解的Bean对象跳过代理流程。需要注意使用AspectJ技术生成的类型如果使用了@Aspect注解,则不会跳过代理:
public boolean isAspect(Class<?> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean compiledByAjc(Class<?> clazz) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith("ajc$")) {
return true;
}
}
return false;
}
【2】获取Bean对象相关的Advisor列表
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
getAdvicesAndAdvisorsForBean
调用栈进入findEligibleAdvisors
方法:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
逻辑较为清晰:
(1) 获取所有的增强逻辑:
Spring启动时会将所有的增强逻辑(Bean定义)添加到IOC中;此时根据Advisor类型和被@Aspect注解从IOC中获取增强对象;
(2) 过滤增强逻辑得到与该Bean类型匹配的增强逻辑列表:
过滤出符合该类型的所有增强逻辑,即根据切面中增强逻辑对应的切点是否匹配该类型;读者查询看部分代码时可以略过引介增强。
(3) 排序增强逻辑列表并返回
【3】生成代理对象
createProxy
方法的主线逻辑如下:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
} else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
return proxyFactory.getProxy(getProxyClassLoader());
}
该方法封装的逻辑输入是Bean对象和增强逻辑,输出是代理对象,即实现了将增强逻辑织入到Bean对象的功能,逻辑结构如下所示:
上述代码在Spring系列-9 Async注解使用与原理文中有较为清晰的解释说明。
最后,建议将上述整体流程再仔细梳理一下,有助于快速定位和解决Spring问题。