美文网首页老男孩的成长之路
Spring IOC中的灵魂伴侣:BeanFactory & A

Spring IOC中的灵魂伴侣:BeanFactory & A

作者: java菲菲 | 来源:发表于2020-02-10 16:42 被阅读0次

    BeanFactory & FactoryBean

    说起Spring中的灵魂伴侣难道不是BeanFactory与FactoryBean吗?ta们两个不仅长相相似,在面试题目中更是形影不离,成双成对。然而事实上二者的关系就像生命中的过客,只是匆匆一眼,便相忘于江湖。
    不过FactoryBean并不孤单,远处的ObjcetFactory遥遥相望,本文中并不准备详细解析这两个接口,不过读者可以把他们两个理解为指定类型Bean的孵化器,我们可以通过这两个接口改变Bean的初始化行为。但是二者还是有很大区别的。

    BeanFactory & ApplicationContext

    日常工作中我们常常将BeanFactory称为容器,而将ApplicationContext称为上下文。

    • 配置类
    package com.spring.container;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan("com.spring.container")
    public class ContainerConfig {
    
    }
    
    • 获取二者信息
    package com.spring.container;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author: Raphael
     */
    @Component
    public class SpringIoc implements BeanFactoryAware, ApplicationContextAware {
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            System.err.println(System.identityHashCode(beanFactory));
            System.out.println(beanFactory.getClass().getName());
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
                throws BeansException {
            System.err.println(System.identityHashCode(applicationContext));
            System.out.println(applicationContext.getClass().getName());
        }
    
    }
    
    • 启动类
    package com.spring.container;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
     * @Author: Raphael
     */
    public class MainContainer {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context =
                    new AnnotationConfigApplicationContext(ContainerConfig.class);
            context.close();
        }
    
    }
    
    • 结果


      image.png

    显而易见二者不仅不是一个对象,类型都不一样。看到这里大多数人的认知已经破碎,因为潜意识中他们认为二者其实是一个事物。事实上人家甚至连继承关系都没有,直接打碎了另一波人的幻想。不信我们看看二者的UML类图。[图片上传失败...(image-5677c9-1581323676178)]


    image.png
    image.png

    几个容器?

    敏锐的人其实已经发现了矛盾,认知中Spring的根容器应该有且只有一个才合理。但是这分明就是两个没有什么关联的对象啊。Spring官方文档如此论述二者的关系:

    简而言之,BeanFactory提供了配置框架和基本功能,ApplicationContext增加了更多针对企业的功能。ApplicationContext是BeanFactory的一个完整的超集。

    按照官方的解释:二者是一个包含与被包含的关系,那么在ApplicationContext中我们可以获得根容器吗?
    上帝说:要有光,于是getAutowireCapableBeanFactory()就来了。

    package com.spring.container;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author: Raphael
     */
    @Component
    public class SpringIoc implements BeanFactoryAware, ApplicationContextAware {
    
        private BeanFactory beanFactory;
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
            System.err.println(System.identityHashCode(beanFactory));
            System.out.println(beanFactory.getClass().getName());
        }
    
        // 直接打印两个对象的比对结果
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
                throws BeansException {
            AutowireCapableBeanFactory factory =
                    applicationContext.getAutowireCapableBeanFactory();
            System.err.println(factory.hashCode());
            System.out.println(factory.getClass().getName());
            System.out.println("二者相等吗:    " + (factory == beanFactory));
        }
    
    }
    

    验证结果完全支持官方说明。

    image.png

    DefaultListableBeanFactory

    官方对他如此定义:

    Spring的{@link ConfigurableListableBeanFactory},{@link BeanDefinitionRegistry}的默认实现
    成熟的bean工厂
    基于bean定义元数据,可通过后处理器进行扩展

    台前光亮的是BeanFactory,负重前行的却是DefaultListableBeanFactory,明明是三个人的电影,我却始终不能有姓名。那么DefaultListableBeanFactory的对象是何时产生的呢?一个新的问题又萦绕在我的心头。答案其实与IOC容器的初始化密不可分,我在这里不详叙了。我们只简单的剖析一下DefaultListableBeanFactory产生对象的心路历程。
    我们在MainContainer中调用了如下构造方法:

    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        this();
        register(annotatedClasses);
        refresh();
    }
    

    因为AnnotationConfigApplicationContext继承了GenericApplicationContext,所以父类的的构造方法也会同时调用,容器对象就在此时诞生。

    public GenericApplicationContext() {
        this.beanFactory = new DefaultListableBeanFactory();
    }
    

    然后refresh()调用obtainFreshBeanFactory()。

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        return getBeanFactory();
    }
    

    getBeanFactory()是AbstractApplicationContext定义的抽象方法。又由GenericApplicationContext实现。

    public final ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }
    

    这里其实就是将自己构造方法产生的对象返回给AnnotationConfigApplicationContext。追踪到这一层的时候,容器对象的身世之谜才终于被我们揭开。其实ApplicationContext之所以有Beanfactory能力就是因为有关容器的操作他都委托给自己内部的容器对象了。举个例子:

    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(name, requiredType);
    }
    

    这里实际上他并没有对此方法有详细的实现,而是通过getBeanFactory()获取自身内部的容器对象,然后交由ta实现。 现在脉络应该足够清晰了。Spring源码是设计模式的集大成者,这里其实运用的就是组合模式

    二者差异

    其实官方有他们二者的总结

    org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基础。BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。 ApplicationContext 是其子接口。

    增加了以下特性:

    • 与Spring的AOP功能轻松集成
    • 消息资源处理(用于国际化)
    • 活动发布
    • 应用层特定的上下文

    作者:颜如玉
    原文链接:https://juejin.im/post/5e3ea6c16fb9a07cb427c5b4

    相关文章

      网友评论

        本文标题:Spring IOC中的灵魂伴侣:BeanFactory & A

        本文链接:https://www.haomeiwen.com/subject/broaxhtx.html