SpringBoot启动流程分析知识点–AOP(一)
一、概述
源码基于SpringBoot 2.7.xx版本
前置知识点–SpringBoot启动流程分析6–run方法之refreshContext()方法
前置知识点–SpringBoot扩展点–BeanPostProcessor
1.1 简介
AOP(Aspect Oriented Programming)是基于切面编程的,可无侵入的在原本功能的切面层添加自定义代码,一般用于日志收集、权限认证等场景。
- Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理。
- SpringBoot 2.x 开始,通过自动装配类AopAutoConfiguration.AspectJAutoProxyingConfiguration.CglibAutoProxyConfiguration,
默认使用 CGLIB 动态代理。 - 在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改,
proxyTargetClass配置已无效。
1.2 分类
- AspectJAwareAdvisorAutoProxyCreator:当使用xml进行定义切面时会注入此类
- AnnotationAwareAspectJAutoProxyCreator:当使用@EnableAspectJAutoProxy注解方式来定义切面时会注入此类(
自动装配AopAutoConfiguration) - InfrastructureAdvisorAutoProxyCreator:当使用@EnableTransactionManagement会注入此类(
自动装配TransactionAutoConfiguration)
1.3 AOP自动装配概览
- AopAutoConfiguration
- CglibAutoProxyConfiguration
- @EnableAspectJAutoProxy
- AspectJAutoProxyRegistrar
- AnnotationAwareAspectJAutoProxyCreator
- AspectJAutoProxyRegistrar
二、自动装配详解
2.1 AopAutoConfiguration自动装配
AopAutoConfiguration
@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
}
AspectJAutoProxyRegistrar
AopAutoConfiguration --> @EnableAspectJAutoProxy --> AspectJAutoProxyRegistrar
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* 注册、升级和配置自动代理创建器依赖对应的proxyTargetClass属性在解析@Configuration类
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注入AnnotationAwareAspectJAutoProxyCreator
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);
}
}
}
}
注:
- AspectJAutoProxyRegistrar 注入了 AnnotationAwareAspectJAutoProxyCreator;
- AnnotationAwareAspectJAutoProxyCreator 是一个 SmartInstantiationAwareBeanPostProcessor,调用时机在Bean初始化之后。
2.2 AnnotationAwareAspectJAutoProxyCreator 使用
调用时机:
DefaultListableBeanFactory.preInstantiateSingletons() --> getBean() --> doGetBean() --> createBean() -->
doCreateBean() --> initializeBean() --> applyBeanPostProcessorsAfterInitialization()执行Bean的后置处理器
2.2.1 applyBeanPostProcessorsAfterInitialization()
调到父类的postProcessAfterInitialization()方法中,然后在wrapIfNecessary()方法中查找Advisor并创建代理类!
AnnotationAwareAspectJAutoProxyCreator --> AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 获取当前bean的key:如果beanName不为空,则以beanName为key,如果为FactoryBean类型,
// 前面还会添加&符号,如果beanName为空,则以当前bean对应的class为key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 判断当前bean是否正在被代理,如果正在被代理则不进行封装
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果它需要被代理,则需要封装指定的bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
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;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this