AutowireCapableBeanFactory探密(1)——为第三方框架赋能

起因

群里有朋友抛出了个问题,问为什么Spring Cache注解未生效,示例代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class DemoApplicationTests {
    @Test
    public void contextLoads() {
        testGetFromDB(1);
        testGetFromDB(1);
        testGetFromDB(1);
    }

    @Cacheable("foo")
    public String testGetFromDB(Integer i) {
        log.info("testGetFromDB...");
        return "retVal" + i;
    }
}

我说,你把@Cacheable注解的方法搬到别的类,然后再试试,结果,好了

大家如果有过类似经验,大概能一眼看出问题所在:Spring AOP方法内部调用,切面逻辑是不生效的(动态代理)。解决方案有两个,一是AopContext#currentProxy,二是在当前类中注入自己,再用该对象进行方法内部调用

但实际上,这里隐藏了一个问题,即便通过上述的两种方式,在本案例中,@Cacheable仍然不会生效。第一种方式会因获取到的proxy对象为空而抛出IllegalStateException异常,第二种方式会因获取不到DemoApplicationTestsBean而抛出NoSuchBeanDefinitionException异常

public static Object currentProxy() throws IllegalStateException {
    Object proxy = currentProxy.get();
    if (proxy == null) {
        throw new IllegalStateException(
                "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
    }
    return proxy;
}
NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.DemoApplicationTests' available

这就很诡异了,为啥会获取不到?难道IOC容器里没有DemoApplicationTests对象?可是,如果没有这个对象,那平时写单测的时候,那些@Resouce@Autowired@Value注解(Spring称为:annotation-driven injection)又是如何生效的,Bean是如何注入的呢

实际上,IOC容器里还真没有DemoApplicationTests对象,也就是说,Spring在IOC容器里压根找不到该Bean,也就不难理解方式一、二都抛了异常。

在一般的认知中,如果某个Bean想使用Spring的功能,需要让Bean注册到Spring IOC容器中,被Spring管理。那么问题就来了,既然Spring 并没有管理DemoApplicationTestsBean,那annotation-driven injection是如何生效的呢?

解决方案

AutowireCapableBeanFactory
  1. BeanFactory的直接子接口,拥有自动装配的能力
  2. 一般不会在普通应用中直接使用AutowireCapableBeanFactory,因此在设计上ApplicationContext们并没有直接实现该接口(画外音:Spring的设计者们不希望用户在应用代码中直接使用AutowireCapableBeanFactory)。尽管如此,却提供了该问该接口的能力:通过ApplicationContext#getAutowireCapableBeanFactory方法可拿到其实例对象
  3. 它真正的作用在于整合其它框架:能让Spring管理的Bean去装配和填充那些不被Spring托管的Bean(wire and populate existing bean instances that Spring does not control the lifecycle of)[重要]

原理

源码基于Spring 5.1.11.RELEASE

当在Junit测试类使用spring-test\spring-boot-test注解(如:@SpringBootTest),并启动测试方法时,Junit框架会准备相关的测试资源,并且加载Spring环境,之后对测试类实例使用Spring进行装配,以保证依赖的服务处于可用状态。测试方法启动过程会经历如下方法:

// org.springframework.test.context.support.DependencyInjectionTestExecutionListener

protected void injectDependencies(TestContext testContext) throws Exception {
    Object bean = testContext.getTestInstance(); // 测试类实例
    Class<?> clazz = testContext.getTestClass(); // 测试类class(DemoApplicationTests)
    AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory();
    // 1. 使用AutowireCapableBeanFactory去装配不受Spring管理的bean
    beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
    // 2. 进行初始化
    beanFactory.initializeBean(bean, clazz.getName() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX);
    
    testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}

从上述源码中可以看到,Bean(DemoApplicationTests)是从testContext中获取,而并没有被Spring管理,然后借助AutowireCapableBeanFactory的能力对Bean进行装配与初始化,让Spring为非Spring托管的Bean赋能

其中,对于@Resouce、@Autowired注入的能力,是通过beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);来实现的,代码如下:

public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException {
    if (autowireMode == AUTOWIRE_CONSTRUCTOR) {
        throw new IllegalArgumentException("AUTOWIRE_CONSTRUCTOR not supported for existing bean instance");
    }
    // Use non-singleton bean definition, to avoid registering bean as dependent bean.
    RootBeanDefinition bd =
            new RootBeanDefinition(ClassUtils.getUserClass(existingBean), autowireMode, dependencyCheck);
    bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    BeanWrapper bw = new BeanWrapperImpl(existingBean);
    initBeanWrapper(bw);
    // 填充属性
    populateBean(bd.getBeanClass().getName(), bd, bw);
}

接着看populateBean方法,该方法作用是为给定的bean填充属性,相信大家已经很熟悉这段代码了,篇幅原因,把非关键代码省略

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // ...(省略)
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            // @Resouce、@Autowired 注解起作用的地方
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
            }
            pvs = pvsToUse;
        }
    }
    // ...(省略)
}

@Resouce@Autowired注解之所以能生效,是因为最终执行了PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);这一行代码。有两个InstantiationAwareBeanPostProcessor分别是CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor,分别用于处理@Resouce@Autowired,达到属性填充的目的

接下来看看AutowireCapableBeanFactory接口,一共有6个属性,15个方法

public interface AutowireCapableBeanFactory extends BeanFactory {

// ---------------------- 属性 ----------------------
    // 装配模式
    int AUTOWIRE_NO = 0;

    int AUTOWIRE_BY_NAME = 1;

    int AUTOWIRE_BY_TYPE = 2;

    int AUTOWIRE_CONSTRUCTOR = 3;

    // @deprecated as of Spring 3.0
    int AUTOWIRE_AUTODETECT = 4;

    String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";

// ---------------------- 方法 ----------------------

    <T> T createBean(Class<T> beanClass) throws BeansException;

    void autowireBean(Object existingBean) throws BeansException;

    Object configureBean(Object existingBean, String beanName) throws BeansException;

    Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

    Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

    void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
            throws BeansException;

    void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;

    Object initializeBean(Object existingBean, String beanName) throws BeansException;

    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException;

    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException;

    void destroyBean(Object existingBean);

    <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;

    Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;

    Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;

    Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;

}

5个AUTOWIRE_*指的是装配模式,接口方法中的autowireMode参数取值就从这5个里边选择

  • int AUTOWIRE_NO = 0;不装配
  • int AUTOWIRE_BY_NAME = 1;根据名称装配
  • int AUTOWIRE_BY_TYPE = 2;根据类型装配
  • int AUTOWIRE_CONSTRUCTOR = 3;根据构造器装配
  • int AUTOWIRE_AUTODETECT = 4;从Spring 3.0就过期了,不作介绍

还有1个属性String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";,该属性是一种约定俗成的用法:以类全限定名+.ORIGINAL 作为Bean Name,用于告诉Spring,在初始化的时候,需要返回原始给定实例,而别返回代理对象

上面的方法看似挺多,经过分类之后,也能比较好理解与记忆。其实从方法的命名上也能看出来各自的作用,这也是Spring框架优秀让人着迷的地方之一:方法命名在很多时候能让人见名知义,足够抽象而又不失准确的概括。按照接口提供能力的粒度,大体上分为三类:

  1. 粗粒度创建、装配Bean的方法
<T> T createBean(Class<T> beanClass) throws BeansException;
void autowireBean(Object existingBean) throws BeansException;
Object configureBean(Object existingBean, String beanName) throws BeansException;
  1. 细粒度控制Bean生命周期的方法(创建、装配Bean的过程涉及Bean的生命周期)。细粒度体现在两个方面,一是提供了可选的装配模式(autowireMode),二是把装配Bean的过程细化为 属性填充(populateBean)初始化(initializeBean) 两个阶段。此外,还提供了BeanPostProcessors的前后置处理逻辑回调,以及销毁Bean的逻辑回调。
Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
Object initializeBean(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
void destroyBean(Object existingBean);
  1. 解析注入点的代理方法,这些方法是辅助类方法,一般不需要直接使用
<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;

上面是官方的分法,下面将按照接口的功能进行划分

  1. 创建Bean
    • createBean(Class<T> beanClass): 用给定的class创建一个Bean实例,完整经历一个Bean创建过程的生命周期节点回调,但不执行传统的autowiring
      • 提供autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck)所有能力
      • 提供initializeBean(Object existingBean, String beanName)所有能力
    • createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck): 与上边类似,主要区别是可以指定autowireMode,即可能会执行传统的autowiring
  2. 属性装配
    • autowireBean(Object existingBean): 主要用于(再次)填充指定Bean被注解的元素或方法(如@Resource @Autowired),不执行传统的autowiring
      • 执行InstantiationAwareBeanPostProcessor的后置处理逻辑,因为这是在populateBean()中执行的
      • 不执行传统的autowiring
      • 不执行initializeBean方法,因此也就不执行标准的BeanPostProcessor
    • autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck): 与上边类似,主要区别是可以指定autowireMode,即可能会执行传统的autowiring
    • autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck): 与上边类似,主要区别是该方法首个入参是Class,框架会帮我们实例化,而上边是已实例化的对象
  3. 初始化
    • initializeBean(Object existingBean, String beanName): 初始化给定的Bean
      • 执行invokeAwareMethods方法(xxxAware,如BeanNameAware)
      • 执行BeanPostProcessor前置处理逻辑
      • 执行invokeInitMethods进行初始化,如InitializingBeanorg#afterPropertiesSet等
      • 执行BeanPostProcessor后置处理逻辑
  4. 配置Bean
    • configureBean(Object existingBean, String beanName): 作为initializeBean的超集存在,功能跟createBean(Class<T> beanClass)类似,但要求beanName对应的BeanDefinition存在,否则会抛出NoSuchBeanDefinitionException异常,使用场景很少
  5. 销毁Bean
    • destroyBean(Object existingBean): 销毁Bean前,执行DestructionAwareBeanPostProcessorDisposableBean等的销毁逻辑,用于资源回收
  6. 对Bean初始化前后应用逻辑处理器
    • applyBeanPostProcessorsBeforeInitialization: 执行BeanPostProcessor前置处理逻辑
    • applyBeanPostProcessorsAfterInitialization: 执行BeanPostProcessor后置处理逻辑
    • applyBeanPropertyValues: 真正执行Bean属性值的填充
  7. 解析方法,辅助类方法
    • resolveNamedBean(Class<T> requiredType): 根据传入的类型,从Spring容器(包括父子容器)中查找出指定类型下唯一的Bean,并将beanNamebeanInstance包装成NamedBeanHolder对象返回。如果该类型下有不止一个对象(非唯一),返回null。该方法其实是BeanFactory#getBean(java.lang.Class<T>)方法的变种,二者底层实现是一样的
    • resolveBeanByName(String name, DependencyDescriptor descriptor): 根据name跟descriptor解析出一个Bean实例,该方法是BeanFactory#getBean(java.lang.String, java.lang.Class<T>)方法的变种
    • resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName): 直接调用下边的方法
    • resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter): 解析Bean依赖,非常重要的方法

AbstractAutowireCapableBeanFactoryAutowireCapableBeanFactory的一个抽象实现类,该类的源码如果不往更底层看,其实还是比较简单(简洁)的,大家以前学习Spring Bean生命周期的时候应该见到过,因此也就不贴源码了,感兴趣的同学可以自行翻看一下

除了Junit,还有Quartz定时任务框架,与Spring集成的时候,也会用上AbstractAutowireCapableBeanFactory。原因是定时任务Job一般需要借助Spring 管理的Service来进行业务逻辑的处理,但Job本身并没有交给Spring管理,而是通过反射生成实例,因此想要借用@Resouce等注解,就得让AbstractAutowireCapableBeanFactory为Job Bean 做自动装配。具体分析可参看此处

总结

Spring 提供了一种机制,能够为第三方框架赋能,让Spring管理的Bean去装配和填充那些不被Spring托管的Bean,这种机制叫AutowireCapableBeanFactory。目前了解到的有两款著名的开源框架JunitQuartz借用了这种机制为自己赋能,达到更好地与Spring契合协作的目的,也从他们的整合姿势中学习到,当我们需要设计一个框架,而且需要拥抱Spring生态,为框架使用者提供更便利的无缝整合,需以微内核 + 插件机制思想,由内核层想办法拿到AutowireCapableBeanFactory,并在构造插件的时候,借助AutowireCapableBeanFactory去为插件赋能,做到无限扩展的可能性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容