Spring源码阅读----Spring IoC之BeanFactory、ApplicationContext

概述

Spring Framework

前面我们通过一个SSMdemo熟悉了一下Spring的工作环境。IOC是Spring框架的一个重要特性,实现IOC的关键是bean,而更关键的是如何bean的管理容器,也就是BeanFactory,现在我们开始从容器模块解读源码。

BeanFactory

我们先通过一个示例来看看BeanFactory如何工作的:

在前面的最后有个例子通过ClassPathXmlApplicationContext解析配置文件,并获取bean。

    public static void main(String[] args) throws ClassNotFoundException {
        //XmlBeanFactory已经被废弃
        //XmlBeanFactory使用方式如下:
        //BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        //这里使用了ClassPathXmlApplicationContext
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        SysAdminUserService s = (SysAdminUserService)context.getBean(SysAdminUserService.class);

        List<SysAdminUser> list = s.selectAll(new SysAdminUser());
        list.stream().forEach(System.out::println);
    }

先来查看XmlBeanFactory的类图,如下:

XmlBeanFactory

再查看ClassPathXmlApplicationContext的类图,如下:

ClassPathXmlApplicationContext

从中可以看到XmlBeanFactory、ClassPathXmlApplicationContext的父类实现的接口中,最顶层都是BeanFactory。

BeanFacotry,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂,在spring-beans包中,它是IOC容器的核心接口,是Spring里面最底层的接口,提供了最简单的容器的功能。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。BeanFactory在启动的时候不会去实例化Bean,有从容器中拿Bean的时候才会去实例化

ApplicationContext接口,应用上下文,在spring-context包中,也是一个IOC容器,比BeanFacotry更高级,它由BeanFactory接口派生而来。ApplicationContext包含BeanFactory的所有功能,并提供了更多其他有用的功能,通常建议使用ApplicationContext优先于BeanFactory。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化。ClassPathXmlApplicationContext类就是其中一个实现ApplicationContext接口的类,类似的类比较常用的还有AnnotationConfigApplicationContext类,前者是根据xml配置文件启动,后者则根据注解配置启动。

我们先看XmlBeanFactory的加载方式,对XmlBeanFactory的讲解不会过多深入,因为这个类已经被废弃,更多的会聚焦和ApplicationContext接口相关的类。
demo源码

//通过XmlBeanFactory来创建BeanFactory 实例
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

执行结果如下:

factory

跟踪源码进入XmlBeanFactory类

@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
        //loadBeanDefinitions
        this.reader.loadBeanDefinitions(resource);
    }
}

可以看到在XmlBeanFactory的构造方法中,创建了XmlBeanDefinitionReader对象,并调用了loadBeanDefinitions方法,其具体源码如下

//XmlBeanDefinitionReader中

 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
    }

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        //校验Resource参数
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Loading XML bean definitions from " + encodedResource);
        }

        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if (!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                //获取Resource的InputSteam流
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {

                    //通过InputSteam构造InputSource
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }

                    //执行doLoadBeanDefinitions方法
                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if (((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }

从上面源码中,我们继续跟踪doLoadBeanDefinitions方法

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            //通过doLoadDocument方法,传入InputSource和Resource得到一个Document对象
            //然后执行registerBeanDefinitions方法去注册各类bean
            Document doc = this.doLoadDocument(inputSource, resource);
            int count = this.registerBeanDefinitions(doc, resource);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + count + " bean definitions from " + resource);
            }

            return count;
        } catch (BeanDefinitionStoreException var5) {
            throw var5;
        } catch (SAXParseException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
        } catch (SAXException var7) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
        } catch (ParserConfigurationException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
        } catch (IOException var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
        } catch (Throwable var10) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
        }
    }

我们主要看核心部分:registerBeanDefinitions方法

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        //这里通过DefaultBeanDefinitionDocumentReader对象来执行registerBeanDefinitions
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }

查看DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法:

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        //获取Document的root元素,即配置文件中的<beans>标签
        //然后执行doRegisterBeanDefinitions方法
        this.doRegisterBeanDefinitions(doc.getDocumentElement());
    }

    //执行doRegisterBeanDefinitions
    protected void doRegisterBeanDefinitions(Element root) {
        //委托给BeanDefinitionParserDelegate对象来处理bean的解析
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            //处理<beans>标签中的profile属性
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }

        //未实现的模板代码,,应该是考虑子类来实现
        this.preProcessXml(root);
        //主要看这步Bean的解析
        this.parseBeanDefinitions(root, this.delegate);
        //未实现的模板代码,应该是考虑子类来实现
        this.postProcessXml(root);
        this.delegate = parent;
    }

从上面的代码继续追踪parseBeanDefinitions方法,此方法未解析beans标签的方法

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            //获取<beans>标签的子节点如<bean>、<context:component-scan>、<import>等等
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element)node;
                    //默认命名空间的标签
                    if (delegate.isDefaultNamespace(ele)) {
                        //解析默认标签
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        //自定义命名空间的标签
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            //自定义命名空间的标签
            delegate.parseCustomElement(root);
        }

    }

这里遍历root节点<beans>标签下的子节点,用命名空间来区分解析各节点元素。
默认命名空间在BeanDefinitionParserDelegate类中定义了,如下:

public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

像<bean>、<import>就是默认的命名空间,<tx:annotation-driven>就是自定义的命名空间

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:jdbc.properties"/>
    </bean>

<import resource="classpath:spring-mybatis.xml" />

那我们继续追踪解析默认命名空间中标签的parseDefaultElement方法

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if (delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if (delegate.nodeNameEquals(ele, "bean")) {
            //这里解析<bean>标签的方法
            this.processBeanDefinition(ele, delegate);
        } else if (delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }

    }

    //processBeanDefinition 处理加工BeanDefinition
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //①这里委托给BeanDefinitionParserDelegate对象来解析Bean标签
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

            try {
                //②核心-注册BeanDefinition
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException var5) {
                this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
            }

            this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

    }

我们先追踪上面①部分,BeanDefinitionParserDelegate类中的parseBeanDefinitionElement方法,看它是如何解析<bean>标签的:

//BeanDefinitionParserDelegate类中
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // 获取 ID 属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // 获取 NAME 属性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            // 名称按照 , ; 进行分割
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            // 如果没有指定 id,将 name 的第一个值作为 id
            beanName = aliases.remove(0);
            if (logger.isTraceEnabled()) {
                logger.trace("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }
        // 默认 null
        if (containingBean == null) {
            // 检查名字是否唯一,如果 id 重复了,将抛出错误
            // 内部 usedNames 是一个 HashSet,将会存储加载过的 name 和 aliases
            checkNameUniqueness(beanName, aliases, ele);
        }

                //前边获取了bean的id、name等属性
                //这里才是核心解析方法:parseBeanDefinitionElement
        // 解析 bean 标签的其它属性
        // 将公共属性放入 AbstractBeanDefinition,具体实现在子类 GenericBeanDefinition
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        // 如果 id 和 name 都是空,那个 spring 会给它生成一个默认的名称
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isTraceEnabled()) {
                        logger.trace("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }
        return null;
    }

 //核心解析方法
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        if (ele.hasAttribute("class")) {
            className = ele.getAttribute("class").trim();
        }

        String parent = null;
        if (ele.hasAttribute("parent")) {
            parent = ele.getAttribute("parent");
        }

        try {
            //通过BeanDefinitionReaderUtils类的createBeanDefinition方法可以看到创建的是GenericBeanDefinition对象
            AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
            this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
            this.parseMetaElements(ele, bd);
            this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            this.parseConstructorArgElements(ele, bd);
            this.parsePropertyElements(ele, bd);
            this.parseQualifierElements(ele, bd);
            bd.setResource(this.readerContext.getResource());
            bd.setSource(this.extractSource(ele));
            AbstractBeanDefinition var7 = bd;
            return var7;
        } catch (ClassNotFoundException var13) {
            this.error("Bean class [" + className + "] not found", ele, var13);
        } catch (NoClassDefFoundError var14) {
            this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
        } catch (Throwable var15) {
            this.error("Unexpected failure during bean definition parsing", ele, var15);
        } finally {
            this.parseState.pop();
        }

        return null;
    }

这里的步骤:
先获取<bean>标签的id、name属性。名称可能是多个使用逗号进行隔开的,相当于alias标签;
然后调用核心解析方法parseBeanDefinitionElement,获取AbstractBeanDefinition(GenericBeanDefinition)对象;
最后返回new BeanDeifinitionHolder,包含了BeanDefinition信息、bean名称、别名数组。

接下来我们追踪上边②部分,核心部分:注册BeanDefinition,调用了BeanDefinitionReaderUtils类中的registerBeanDefinition方法:

    public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }

    }

小结一下

这里通过XmlBeanFactory来创建BeanFactory 实例的过程,就是通过XmlBeanFactory构造方法,开始解析XML配置文件,生成Docuemnt对象,解析Document中的标签,按标签的类型来进行分类解析,有些标签需要委托给BeanDefinitionParserDelegate对象解析,最后将BeanDefinition注册到DefaultListableBeanFactory的beanDefinitionMap中;或者SimpleAliasRegistry中的aliasMap。
不论是使用解析Xml还是使用ComponentScan等解析的BeanDefinition都需要进行注册,这样BeanFactory才能统一管理这些bean。

ApplicationContext

接下来我们解析一下ApplicationContext,这个接口的实现类非常多,不过不管是XmlWebApplicationContext、ClassPathXmlApplicationContext,还是AnnotationConfigApplicationContext,这些都是AbstractApplicationContext的派生类,模板方法refresh方法在该类中定义,核心逻辑也都是在 AbstractApplicationContext 抽象类中,另外还需要分析ConfigurableApplicationContext接口,它继承了ApplicationContext接口和Lifecycle接口(Spring容器的生命周期,比较简单的生命周期,定义了启动,停止、是否在运行中三个方法)。

demo中的代码如下:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

执行结果如下:

context

跟踪源码进入ClassPathXmlApplicationContext的构造函数:

    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
    }

    //调用核心方法
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) 
                                                                 throws BeansException {
        super(parent);
        //设置一下传入的xml配置文件路径,赋值给configLocations属性
        this.setConfigLocations(configLocations);
        if (refresh) {
            //核心方法:ApplicationContext初始化
            this.refresh();
        }
    }

先看一下setConfigLocation方法,跟踪源码发现它在AbstractRefreshableConfigApplicationContext类中,源码如下:

    public void setConfigLocation(String location) {
        this.setConfigLocations(StringUtils.tokenizeToStringArray(location, ",; \t\n"));
    }

    public void setConfigLocations(@Nullable String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];

            for(int i = 0; i < locations.length; ++i) {
                //循环处理每个location字符串
                this.configLocations[i] = this.resolvePath(locations[i]).trim();
            }
        } else {
            this.configLocations = null;
        }
    }

    //处理ocation字符串的方法
    protected String resolvePath(String path) {
        //Spring容器的Environment在这里通过getEnvironment方法初始化
        //然后调用resolveRequiredPlaceholders方法处理字符串
        return this.getEnvironment().resolveRequiredPlaceholders(path);
    }

继续查看getEnvironment方法,它来自AbstractApplicationContext类:

    public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
            //创建一个StandardEnvironment对象
            this.environment = this.createEnvironment();
        }

        return this.environment;
    }

    protected ConfigurableEnvironment createEnvironment() {
        return new StandardEnvironment();
    }

这里我们获取了一个StandardEnvironment对象,StandardEnvironment类继承自AbstractEnvironment,在开发中可以获取很多环境信息,对项目执行时监控非常有帮助。
查看一下StandardEnvironment类关系图:

StandardEnvironment

可以看到它的父类实现了Enviroment、ConfigurablePropertyResolver接口,它们又继承了PropertyResolver接口。查看接口里的方法可以发现Enviroment跟Spring中各类 profiles处理有关、ConfigurablePropertyResolver跟Spring中的类型转换ConversionService有关、PropertyResolver接口跟Spring中的Property处理有关。
我们项目中经常会使用不同的运行环境,dev,test 或者 prod等,这个时候加载的配置文件和属性应该有所不同,这个时候就需要使用到 Environment 来进行区分。

我们需要了解Spring 环境和属性是由四个部分组成:

  • Environment : 环境
  • Profile : 配置文件,可以理解为,容器里多个配置组别的属性和 bean,只有激活的 profile,它对应的组别属性和 bean 才会被加载
  • PropertySource : 属性源
  • PropertyResolver : 属性解析器,这个用途就是解析属性

Profile 相信大家都很熟悉了,在springboot项目里我们会使用spring.profiles.active=dev来指定profile配置文件,Spring中原始的方式是在xml配置文件里设置,如下:

<!-- 测试环境配置文件 -->
<beans profile="test">
    <context:property-placeholder location="classpath:test/*.properties, classpath:common/*.properties" />
</beans>

<!-- 生产环境配置文件 -->
<beans profile="prod">
    <context:property-placeholder location="classpath:production/*.properties, classpath:common/*.properties" />
</beans>

<!-- 开发环境配置文件 -->
<beans profile="dev">
    <context:property-placeholder location="classpath:dev/*.properties, classpath:common/*.properties" />
</beans>

然后使用的时候可以使用以下两种方法:
①在 web.xml 中设置

<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>

②直接在代码中设置

//容器初始化后
context.getEnvironment().setActiveProfiles("dev");

PropertySources
在StandardEnvironment类中我们可以看到customizePropertySources方法对PropertySources的操作

    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new MapPropertySource("systemProperties", this.getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
    }

MutablePropertySources 的底层是一个CopyOnWriteArrayList,容器初始化之后可以在代码中获取:

((MutablePropertySources)((StandardEnvironment)context.environment).propertySources).propertySourceList

好了,配置信息和环境的部分就讲解到这里。

回到上边,要开始refresh方法了,Bean 的解析和注册都在这里

在设置了配置信息之后,接下来就需要对ApplicationContext进行初始化操作了,也就是执行refresh方法,这个方法在AbstractApplicationContext类中实现:

//AbstractApplicationContext类中

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            // 1为更新准备上下文
            //对系统属性或者环境变量的校验,设定一些标志
            this.prepareRefresh();

            // 2告诉子类去更新它们的 bean factory
            //对xml配置的解析并注册bean到bean factory
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

            // 3对BeanFactory进行各种使用前的扩展准备
            this.prepareBeanFactory(beanFactory);

            try {
                // 模板方法,交给子类实现
                //允许子类对BeanFactory进行一些后期扩展
                this.postProcessBeanFactory(beanFactory);

                // 4 引用执行注册过的beanFactory后处理器
                this.invokeBeanFactoryPostProcessors(beanFactory);

                // 注册拦截bean创建的bean处理器
                //这里只是注册,真正调用是在getBean对时候
                this.registerBeanPostProcessors(beanFactory);

                //初始化消息资源
                this.initMessageSource();

                //初始化应用消息广播器
                this.initApplicationEventMulticaster();

                 // 模板方法,交给子类实现,允许子类来初始化其他Bean
                this.onRefresh();
                
                // 检查并注册监听器
                //在所有注册对bean中寻找Lestener bean,注册到消息广播器中
                this.registerListeners();

                // 5实例化非懒加载的单例
                this.finishBeanFactoryInitialization(beanFactory);
                //完成刷新过程
                //通知生命周期处理器刷新过程,发送广播事件
                //保证对应的监听器可以做进一步的逻辑处理
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

整个过程可以按代码中的注释下来,比较清晰。

  • 注释1,对系统属性或者环境变量的校验,设定一些标志。这步比较简单具体看validateRequiredProperties方法,它具体在AbstractPropertyResolver这个类中实现,主要是遍历属性,空的话记录异常。
  • 注释2,获取bean factory,即bean容器,并注册bean,具体看obtainFreshBeanFactory方法
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //BeanFactory在这步创建,并解析注册bean
        //具体在AbstractRefreshableApplicationContext类中实现该方法
        this.refreshBeanFactory();
        //获取BeanFactory
        return this.getBeanFactory();
    }

跟踪代码进入AbstractRefreshableApplicationContext类的refreshBeanFactory方法

    protected final void refreshBeanFactory() throws BeansException {
        //如果存在bean容器了,清理掉,并关闭已有的bean容器
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            //创建一个DefaultListableBeanFactory对象,它就是新的bean容器
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            //对BeanFactory的扩展,添加了@Qualifier和@Autowired的支持
            this.customizeBeanFactory(beanFactory);
            //这里就是注册bean了,跟之前分析BeanFactory的时候类似
            this.loadBeanDefinitions(beanFactory);
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

这一步非常重要,主要过程如下:
①先判断已有容器,如果存在,则清理掉,并关闭。
②创建一个DefaultListableBeanFactory对象,它就是新的bean容器
③设定序列号ID
④自定义BanFactory
⑤开始加载,即bean注册(核心方法)
⑥使用全局变量beanFactoryMonitor,记录BeanFactory实例

这里具体解析一下步骤⑤,loadBeanDefinitions方法在AbstractXmlApplicationContext类中实现

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 创建一个XmlBeanDefinitionReader 对象
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            // 最终委托给XmlBeanDefinitionReader来执行loadBeanDefinitions
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            // 最终委托给XmlBeanDefinitionReader来执行loadBeanDefinitions
            reader.loadBeanDefinitions(configLocations);
        }

    }

通过上面的源码我们可以看到AbstractRefreshableApplicationContext类中的loadBeanDefinitions方法为模板方法,交给子类AbstractXmlApplicationContext来实现,它通过创建XmlBeanDefinitionReader对象,来执行XmlBeanDefinitionReader对象的loadBeanDefinitions方法,这里就跟之前介绍的BeanFactory加载注册bean步骤一样。
最后可以通过getBeanFactory()来获取BeanFactory。
※扩展说明:这样AbstractApplicationContext就可以全部委托getBeanFactory方法来完成bean的管理配置,如下:

    //类似的方法都是通过委托getBeanFactory来实现的
    public Object getBean(String name) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(name);
    }

那这个getBeanFactory方法,在AbstractApplicationContext中只是个模板方法,具体由其子类AbstractRefreshableApplicationContext实现:

    //看,这里的beanFactory就是上面步骤6里面设置的BeanFactory实例
    public final ConfigurableListableBeanFactory getBeanFactory() {
        synchronized(this.beanFactoryMonitor) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
            } else {
                return this.beanFactory;
            }
        }
    }

好了,obtainFreshBeanFactory方法解析完了,现在知道AbstractApplicationContext为啥具备bean管理配置的能力了吧?那继续看上面的注释3

  • 注释3,prepareBeanFactory方法,准备类加载器的环境,对前面获取到的 beanFactory进行各类扩展设置。包括 ClassLoader类加载器, post-processors后处理等的设置,增加对SPEL语言的支持、将相关环境变量及属性注册以单例模式注册等内容。

  • 注释4,invokeBeanFactoryPostProcessors
    实例化并调用所有注册的 BeanFactoryPostProcessor,这些是后处理器,处理类型是 BeanFactory, ApplicationContext容器允许在实例化 bean 前,读取 bean 信息和修改它的属性。就是说在实例化前,给用户最后一次机会去修改 bean 信息。(Spring里还有很多这类PostProcessor后置处理器,比如BeanPostProcessor等等,还是比较重要的,这里先不展开讲这些后处理器。)

  • 注释5,finishBeanFactoryInitialization
    完成 bean 容器的初始化,实例化所有剩余的(非非延迟加载)单例的bean

/**
     * Finish the initialization of this context's bean factory,
     * initializing all remaining singleton beans.
     */
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }

这里看到freezeConfiguration方法,是对bean定义的冻结,注册后的bean不可改了;preInstantiateSingletons方法就是实例化这些bean,这就是所谓的ApplicationContext容器初始化时,默认会将所有的单例bean提前进行初始化,就在这个方法。和BeanFactory不同,BeanFactory中的bean是在获取的时候才初始化。
查看PreInstantiateSingletons方法源码:

public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Pre-instantiating singletons in " + this);
        }

        List<String> beanNames = new ArrayList(this.beanDefinitionNames);
        Iterator var2 = beanNames.iterator();

        while(true) {
            String beanName;
            Object bean;
            do {
                while(true) {
                    RootBeanDefinition bd;
                    do {
                        do {
                            do {
                                if (!var2.hasNext()) {
                                    var2 = beanNames.iterator();

                                    while(var2.hasNext()) {
                                        beanName = (String)var2.next();
                                        //注释:获取单例bean的实例
                                        Object singletonInstance = this.getSingleton(beanName);
                                        if (singletonInstance instanceof SmartInitializingSingleton) {
                                            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
                                            if (System.getSecurityManager() != null) {
                                                AccessController.doPrivileged(() -> {
                                                    smartSingleton.afterSingletonsInstantiated();
                                                    return null;
                                                }, this.getAccessControlContext());
                                            } else {
                                                smartSingleton.afterSingletonsInstantiated();
                                            }
                                        }
                                    }

                                    return;
                                }

                                beanName = (String)var2.next();
                                bd = this.getMergedLocalBeanDefinition(beanName);
                            } while(bd.isAbstract());
                        } while(!bd.isSingleton());
                    } while(bd.isLazyInit());

                    if (this.isFactoryBean(beanName)) {
                        bean = this.getBean("&" + beanName);
                        break;
                    }

                    this.getBean(beanName);
                }
            } while(!(bean instanceof FactoryBean));

            FactoryBean<?> factory = (FactoryBean)bean;
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                SmartFactoryBean var10000 = (SmartFactoryBean)factory;
                ((SmartFactoryBean)factory).getClass();
                isEagerInit = (Boolean)AccessController.doPrivileged(var10000::isEagerInit, this.getAccessControlContext());
            } else {
                isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
            }

            if (isEagerInit) {
                this.getBean(beanName);
            }
        }
    }

最后,注册的最后一步是在finally代码块中的resetCommonCaches方法,用来清除缓存。

好了,以上就是ApplicationContext容器初始化的步骤。

通过BeanFactory容器和ApplicationContext容器的初始化步骤对比,可以知道两者的区别了吗?

总结

这里通过debug跟踪不同的容器初始化的步骤,这只是Spring的开始,通过对比知道两者的联系和区别。
BeanFactory是Spring IOC的基础,它是所有容器接口的父类。
ApplicationContext接口是BeanFactory的子类,对BeanFactory进行了扩展,使得ApplicationContext具有更多的功能;
ApplicationContext容器的初始化中包含了BeanFactory的初始化以及对BeanFactory的扩展,它通过getBeanFactory方法,把bean配置管理委托给BeanFactory;
ApplicationContext是初始化的时候就初始化所有的单例bean,BeanFactory中的bean是在获取的时候才初始化;
ApplicationContext增加了SPEL语言的支持(#{xx.xx}等配置)、 消息发送、响应机制(ApplicationEventPublisher)、支持了@Qualiiar和@Autowired等注解。
※细心的同学可能发现在上面的执行图中,BeanFactory里的bean定义数量有16个,而ApplicationContext立却有17个。原因就在于ApplicationContext容器对BeanFactory进行了扩展,它所注册的所有后处理器会遍历执行。MapperScannerConfigurer就是其中一个PostProcessor,而BeanFactory却没有这个功能。

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

推荐阅读更多精彩内容