摘要:

Spring Framwork的两大核心技术就是IOCAOPAOPSpring的产品线中有着大量的应用。如果说反射是你通向高级的基础,那么代理就是你站稳高级的底气。AOP的本质也就是大家所熟悉的CGLIB动态代理技术,在日常工作中想必或多或少都用过但是它背后的秘密值得我们去深思。本文主要从Spring AOP运行过程上,结合一定的源码整体上介绍Spring AOP的一个运行过程。知其然,知其所以然,才能更好的驾驭这门核心技术。

    @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
//表明该类采用CGLIB代理还是使用JDK的动态代理
boolean proxyTargetClass() default false;
/**
* @since 4.3.1 代理的暴露方式:解决内部调用不能使用代理的场景 默认为false表示不处理
* true:这个代理就可以通过AopContext.currentProxy()获得这个代理对象的一个副本(ThreadLocal里面),从而我们可以很方便得在Spring框架上下文中拿到当前代理对象(处理事务时很方便)
* 必须为true才能调用AopContext得方法,否则报错:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
*/
boolean exposeProxy() default false;
}

所有的EnableXXX驱动技术都得看他的@Import,所以上面最重要的是这一句@Import(AspectJAutoProxyRegistrar.class),下面看看它

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
AspectJAutoProxyRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//注册了一个基于注解的自动代理创建器 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
//表示强制指定了要使用CGLIB
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//强制暴露Bean的代理对象到AopContext
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}

AspectJAutoProxyRegistrar是一个项容器注册自动代理创建器

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

说明spring容器的注解代理创建器就是AnnotationAwareAspectJAutoProxyCreator

    @Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//这里如果我们自己定义了这样一个自动代理创建器就是用我们自定义的
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
/**
*用户注册的创建器,必须是InfrastructureAdvisorAutoProxyCreator
*AspectJAwareAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator之一
*/
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
//若用户自己没有定义,那就用默认的AnnotationAwareAspectJAutoProxyCreator
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
//此处注意,增加了一个属性:最高优先级执行,后面会和@Async注解一起使用的时候起关键作用
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}

我们就成功的注入了一个BeanAnnotationAwareAspectJAutoProxyCreator 基于注解的自动代理创建器

Spring中自动创建代理器

由此可见,Spring使用BeanPostProcessor让自动生成代理。基于BeanPostProcessor的自动代理创建器的实现类,将根据一些规则在容器实例化Bean时为匹配的Bean生成代理实例。

AbstractAutoProxyCreator是对自动代理创建器的一个抽象实现。最重要的是,它实现了SmartInstantiationAwareBeanPostProcessor接口,因此会介入到Spring IoC容器Bean实例化的过程。

SmartInstantiationAwareBeanPostProcessor继承InstantiationAwareBeanPostProcessor所以它最主要的 职责是在bean的初始化前,先会执行所有的InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,谁第一个返回了不为nullBean,后面就都不会执行了 。然后会再执行BeanPostProcessor#postProcessAfterInitialization

    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;
}

说明:这个方法是spring的三级缓存中的其中一环,当你调用Object earlySingletonReference = getSingleton(beanName, false);时候就会触发,其实还有一个地方exposedObject = initializeBean(beanName, exposedObject, mbd);也会触发导致返回一个代理对象。

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}

强调: 这2个地方虽然都有后置增强的作用,但是@Async所使用的AsyncAnnotationBeanPostProcessor不是SmartInstantiationAwareBeanPostProcessor的实现类,所以此处会导致@Transactional@Async处理循环依赖时候的不一致性。对于循环依赖后续会有单独章节进行分享。

AbstractAdvisorAutoProxyCreator

如何创建代理对象后续文章在进行分析。

Springboot源码分析之EnableAspectJAutoProxy的更多相关文章

  1. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  2. Springboot源码分析之项目结构

    Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ...

  3. 从SpringBoot源码分析 配置文件的加载原理和优先级

    本文从SpringBoot源码分析 配置文件的加载原理和配置文件的优先级     跟入源码之前,先提一个问题:   SpringBoot 既可以加载指定目录下的配置文件获取配置项,也可以通过启动参数( ...

  4. springboot源码分析-SpringApplication

    SpringApplication SpringApplication类提供了一种方便的方法来引导从main()方法启动的Spring应用程序 SpringBoot 包扫描注解源码分析 @Spring ...

  5. Springboot源码分析之jar探秘

    摘要: 利用IDEA等工具打包会出现springboot-0.0.1-SNAPSHOT.jar,springboot-0.0.1-SNAPSHOT.jar.original,前面说过它们之间的关系了, ...

  6. Springboot源码分析之代理三板斧

    摘要: 在Spring的版本变迁过程中,注解发生了很多的变化,然而代理的设计也发生了微妙的变化,从Spring1.x的ProxyFactoryBean的硬编码岛Spring2.x的Aspectj注解, ...

  7. Springboot源码分析之事务拦截和管理

    摘要: 在springboot的自动装配事务里面,InfrastructureAdvisorAutoProxyCreator ,TransactionInterceptor,PlatformTrans ...

  8. SpringBoot源码分析(二)启动原理

    Springboot的jar启动方式,是通过IOC容器启动 带动了Web容器的启动 而Springboot的war启动方式,是通过Web容器(如Tomcat)的启动 带动了IOC容器相关的启动 一.不 ...

  9. Springboot源码分析之Spring循环依赖揭秘

    摘要: 若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效.或许刚说到这,有的小伙伴就会大惊失色了.Spring不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我 ...

随机推荐

  1. 各类最短路算法基本模板-C++

    原文转自:https://blog.csdn.net/changjiale110/article/details/77394650 感谢. #define Max 0x3f3f3f3f #define ...

  2. [记录]inotifywait+rsync脚本和sersync2服务检测的脚本

    1)inotifywait+rsync脚本: #!/bin/bash src=/data/ # 需要同步的源路径 des=data # 目标服务器上 rsync --daemon 发布的名称,rsyn ...

  3. SpringBoot工程热部署

    SpringBoot工程热部署 1.在pom文件中添加热部署依赖 <!-- 热部署配置 --> <dependency> <groupId>org.springfr ...

  4. 网页判断浏览器是否安装flash

    <script> //检验flash版本 以及falsh是否安装 function detectFlash() { try { if(navigator.mimeTypes.length& ...

  5. F#周报2019年第29期

    新闻 ML.NET 1.2发布,包含Model Builder升级 NuGet.org上现在显示GitHub的使用情况 微基准测试设计准则 为线程添加mono.wasm支持 Haskell--经验总结 ...

  6. PC端触底效果反复触发的解决方案

    最近在做一个PC端的项目,要求是在滑动到页面的底部的时候就动态的加载下一页的数据,代码实现思路如下: 首先,我们需要知道浏览器中有三个高度,分别是屏幕高度(outerHeight),文档容器高度(in ...

  7. Android的简述2

    android提供了三种菜单类型,分别为options menu,context menu,sub menu. options menu就是通过按home键来显示,context menu需要在vie ...

  8. 使用阿里云对docker拉取镜像加速

    使用docker的时候,总是需要去search镜像,使用国外的源下载太慢,还有诸多的限制,无意中发现可以使用阿里云进行加速,实测有用,废话少说,操作如下: 1.打开阿里云控制台,没有的可以用淘宝账号或 ...

  9. Js面向对象构造函数继承

    构造函数继承 <!-- 创建构造函数 --> function Animal(){ this.species= '动物'; } function Dog(name,color){ this ...

  10. How to check all timestamps of a file

    A friend of mine she asked me how to check all timestamps of a file on an NTFS volume. She did not h ...