所有文章

https://www.cnblogs.com/lay2017/p/11478237.html

正文

spring的两大核心就是ioc和aop。在关于ioc依赖注入的文章中,我们了解了如何根据BeanDefinition创建Bean,然后在BeanPostProcessor中处理@Autowired和@Resource两个注解,自动注入Bean。

本文将讲解另外一块核心内容,aop切面。

AOP自动配置

首先,aop切面基于springboot的自动配置。如果不了解自动配置,请参考自动配置机制的文章。

为此,我们先找到aop自动配置的类AopAutoConfiguration

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration { @Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration { } @Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
public static class CglibAutoProxyConfiguration { } }

我们将以Cglib为主要的了解对象,可以看到CglibAutoProxyConfiguration上注解了一个@EnableAspectJAutoProxy,它意味着开启切面代理,我们打开该注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy { //
}

我们看到,@Import注解导入了AspectJAutoProxyRegistrar。在自动配置的文章中,我们了解到ConfigurationClassParser将会处理@Import注解。这里我们不再关注如何处理@Import注解,直接打开AspectJAutoProxyRegistrar类看看

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注册自动切面代理的创建器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); // 省略
} }

跟进registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}

继续跟进registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

我们看到,调用registerOrEscalateApcAsRequired方法指定了一个类AnnotationAwareAspectJAutoProxyCreator,这个类将作为创建代理的类

跟进registerOrEscalateApcAsRequired方法

private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { //
// 构建BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册到Bean容器
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}

可以看到,最终是注册到了Bean容器中,作为BeanDefinition存在。我们可以认为aop的自动配置过程就是为了创建AnnotationAwareAspectJAutoProxyCreator这个类的BeanDefinition。

注册AnnotationAwareAspectJAutoProxyCreator

首先,我们先看看AnnotationAwareAspectJAutoProxyCreator的继承结构

可以看到,AnnotationAwareAspectJAutoProxyCreator最终是实现了BeanPostProcessor,那BeanPostProcessor是什么时候被创建为Bean的呢?

这里,我们得回到refresh容器过程的refresh方法中,跟进AbstractApplicationContext的refresh方法

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// try {
// // 注册BeanDefinition到容器
invokeBeanFactoryPostProcessors(beanFactory); // 注册后置处理器到容器
registerBeanPostProcessors(beanFactory); //
}
//
}
}

在注册BeanDefinition之后,就会把BeanPostProcessor的BeanDefinition转化为了Bean注册到容器中。registerBeanPostProcessors结束以后,AnnotationAwareAspectJAutoProxyCreator就以Bean的形式存在于BeanFactory中了。

触发AnnotationAwareAspectJAutoProxyCreator

接着,我们再看AnnotationAwareAspectJAutoProxyCreator被注册为Bean以后,是在什么位置被触发的。前面,ioc依赖注入的文章中我们提到过createBean方法,将会根据BeanDefinition创建Bean。

跟进AbstractAutowireCapableBeanFactory的createBean方法

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { // try {
// BeanPostProcessors 调用
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
// try {
// 创建实例对象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//
return beanInstance;
}
//
}

我们看到,在doCreateBean之前,先触发resolveBeforInstantiation方法,调用了AnnotationAwareAspectJAutoProxyCreator。

切面解析

跟进resolveBeforeInstantiation

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
//
}
}
//
}
return bean;
}

继续跟进applyBeanPostProcessorBeforeInstantiation方法

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 触发postProcesBeforeInstantiation
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}

继续跟进AbstractAutoProxyCreator的postProcessBeforeInstantiation方法

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
} //
return null;
}

isInfrastructureCLass将返回false,跟进AspectJAwareAdvisorAutoProxyCreator的shouldSkip

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// 返回所有的通知,如@Before @After @AfterThrowing @Round
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}

这里的findCandidateAdvisors方法,将会从beanFactory中获得注解了@Aspect的类元数据,然后获取其中定义的Advisor。

到这一步,我们就已经获得了完成了切面部分的解析工作。

代理增强

回到AbstractAutowireCapableBeanFactory的createBean方法

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { // try {
// BeanPostProcessors 调用
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
// try {
// 创建实例对象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//
return beanInstance;
}
//
}

获取了Advisor,那么再看看Advisor被增强到Bean上的过程,跟进doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// if (instanceWrapper == null) {
// 创建实例对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Object exposedObject = bean;
try {
// 自动注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean,创建代理的入口
exposedObject = initializeBean(beanName, exposedObject, mbd);
} //
return exposedObject;
}

我们找到initializeBean,这个初始化Bean的入口,将从这里开始关注代理增强部分,跟进initializeBean方法

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
//
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}

继续跟进applyBeanPostProcessorsAfterInitialization方法

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

这里将会调用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;
}

继续跟进wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// // 获取适用于当前Bean的Advisors
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;
}

getAdvicesAndAdvisorsForBean方法将会获取到可以增强到该Bean的Advisor,然后createProxy将会创建代理类,并一路返回,如果是单例,则注册到缓存中

跟进createProxy看看创建代理

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) { // // 获取可用的advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors); //
// 创建并返回代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}

跟进getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

createAopProxy将会根据条件返回Cglib实现或者jdk动态代理的实现,然后调用它们的getProxy方法去获取代理对象

总结

本文省略了不少细节内容,大体逻辑是从:aop自动配置 -> 解析@Aspect切面对象 -> 代理增强,这么一个逻辑行文的。

自动配置的核心就是为了导入一个AnnotationAwareAspectJAutoProxyCreator,该类实现了BeanPostProcessor。所以在创建Bean实例对象之前会触发解析@Aspect切面对象,获取Advisor。在生成Bean实例对象之后,会再触发该类对Bean实例对象做代理增强,增强的Advisor来自之前的解析结果。代理增强的实现有cglib和jdk动态代理两种。

最后,增强过的代理Bean如果是单例,将跟以前一样添加到缓存对象中。

springboot启动流程(十一)aop切面处理过程的更多相关文章

  1. springboot启动流程(目录)

    springboot出现有段时间了,不过却一直没有怎么去更多地了解它.一方面是工作的原因,另一方面是原来觉得是否有这个必要,但要持续做java似乎最终逃不开要去了解它的命运.于是考虑花一段时间去学习一 ...

  2. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  3. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot启动流程及其原理

    Spring Boot.Spring MVC 和 Spring 有什么区别? 分别描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等:但他们的 ...

  5. SpringBoot 启动流程

    SpringBoot 启动流程 加载 resources/META-INF/spring.factories 中配置的 ApplicationContextInitializer 和 Applicat ...

  6. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  7. SpringBoot启动流程解析

    写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...

  8. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  9. SpringBoot启动流程分析(二):SpringApplication的run方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  10. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

随机推荐

  1. QML发布程序

    如果是在windows系统下,则最终打包成exe windeployqt  xxx.exe  -qmldir  C:\Qt\Qt5.9.6\5.9.6\mingw53_32\qml 注意使用Qt自己的 ...

  2. WinSCP 上传文件至Cetos 7 后用户无权限

    WinSCP是一个支持SSH的SCP文件传输软件. 可以用Windows环境向Linux环境传输文件,今天给新的Elasticsearch 服务器(cetos 7 )部署新的集群节点的时候,发现传输后 ...

  3. mysql正则替换某个字段值里面的某个字符串

    sql语句如下: UPDATE `ccvms_video` SET title=REPLACE(title, "最", "相对") WHERE title LI ...

  4. html转图片网页截屏(二)PhantomJS

    关于PhantomJS PhantomJS 是一个基于WebKit的服务器端 JavaScript API.它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS ...

  5. Windows Server 2012 R2蓝屏

    最近发现某个医院客户Windows Server 2012 R2 DataCenter的系统频繁蓝屏重启,重启没过一天再一次出现,反反复复折腾好几天,提示信息ntoskrnl.exe.mssmbios ...

  6. Npcap.例子(raw tcp syn)

    1.来自:winpcap实现syn攻击 - 125096 - CSDN博客.html(https://blog.csdn.net/qq125096885/article/details/5178452 ...

  7. JS CSS写下拉菜单 竖行

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 【转】git2.9.2使用总结

    git2.9.2使用总结 1.系统:Windows7 2.git版本:2.9.2 由于我的git版本是最新版,后面我出现的坑就是最新版本的问题. 3.托管环境:开源中国的码云 . 使用步骤 1.在码云 ...

  9. eNSP下利用三层交换机实现VLAN间路由

    我们可以通过配置VLANif接口的方式来使交换机实现路由交换. 而VLANif接口是基于网络层的接口,可以配置ip地址 拓扑图如下

  10. EMR目录

    EMR目录: E-MapReduce 是阿里云上的开源大数据平台,用户可以登录集群主节点查看相关安装路径. 登录后也可以使用 env |grep xxx 查看. 或者,在EMR Hadoop集群,切换 ...