Spring 的 Bean 的加载过程

概述

首先,概括的描述一下 Spring 背后的操作:

  1. 通过注解和xml的方式,将定义的bean(如 loginService 和 loginResource) 解析成 Spring 内部的 BeanDefinition
  2. 以 beanName(如 loginService) 为 key,BeanDefinition(如 loginService 相应的 BeanDefinition) 为 value 存储到 DefaultListableBeanFactory 中的 beanDefinitionMap (其实就是一个 ConcurrentHashMap) 中。
  3. 同时将 beanName 存入 beanDefinitionNames(List 类型) 中,然后遍历 beanDefinitionNames 中的 beanName,进行 bean 的实例化并填充属性。
  4. 在实例化的过程中,如果有依赖没有被实例化将先实例化其依赖,然后实例化本身,实例化完成后将实例存入单例 bean 的缓存中。
  5. 当调用 getBean 方法时,到单例 bean 的缓存中查找,如果找到并经过转换后返回这个实例 (如 LoginResource 的实例),之后就可以直接使用了。

BeanDefinition

BeanDefinition有很多属性和方法都很熟悉,例如类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等,其实就是将Bean的定义信息存储到这个BeanDefinition相应的属性中,后面对Bean的操作就直接对BeanDefinition进行,例如拿到这个BeanDefinition后,可以根据里面的类名、构造函数、构造函数参数,使用反射进行对象创建。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

	int ROLE_APPLICATION = 0;

	int ROLE_SUPPORT = 1;

	int ROLE_INFRASTRUCTURE = 2;

	void setParentName(@Nullable String parentName);

	@Nullable
	String getParentName();

	void setBeanClassName(@Nullable String beanClassName);

	@Nullable
	String getBeanClassName();

	void setScope(@Nullable String scope);

	@Nullable
	String getScope();

	void setLazyInit(boolean lazyInit);

	boolean isLazyInit();

	void setDependsOn(@Nullable String... dependsOn);

	@Nullable
	String[] getDependsOn();

	void setAutowireCandidate(boolean autowireCandidate);

	boolean isAutowireCandidate();

	void setPrimary(boolean primary);

	boolean isPrimary();

	void setFactoryBeanName(@Nullable String factoryBeanName);

	@Nullable
	String getFactoryBeanName();

	void setFactoryMethodName(@Nullable String factoryMethodName);

	@Nullable
	String getFactoryMethodName();

	ConstructorArgumentValues getConstructorArgumentValues();

	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	MutablePropertyValues getPropertyValues();

	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	void setInitMethodName(@Nullable String initMethodName);

	@Nullable
	String getInitMethodName();

	void setDestroyMethodName(@Nullable String destroyMethodName);

	@Nullable
	String getDestroyMethodName();

	void setRole(int role);

	int getRole();

	void setDescription(@Nullable String description);

	@Nullable
	String getDescription();

	boolean isSingleton();

	boolean isPrototype();

	boolean isAbstract();

	@Nullable
	String getResourceDescription();

	@Nullable
	BeanDefinition getOriginatingBeanDefinition();

}

BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如 ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。
在这里插入图片描述
BeanDefinition继承了AttributeAccessor,说明它具有处理属性的能力;
BeanDefinition继承了BeanMetadataElement,说明它可以持有Bean元数据元素,作用是可以持有XML文件的一个bean标签对应的Object。
在这里插入图片描述

BeanFactory

在Spring容器启动的过程中,会将类解析成Spring内部的BeanDefinition结构,并将BeanDefinition存储到一个叫DefaultListableBeanFactory中。
在这里插入图片描述
可以看到 DefaultListableBeanFactory 间接实现了BeanFactory接口和AliasRegistry,先来分析一下这个BeanFactory。

public interface BeanFactory {
	String FACTORY_BEAN_PREFIX = "&";
	
	Object getBean(String name) throws BeansException;

	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

	boolean containsBean(String name);

	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);
}

从接口中的方法名可以很容易的理解每个方法的意图,最常用或者最常见的就是getBean方法,用于获取Bean的实例。BeanFactory是用于访问Spring Bean容器的根接口,是一个单纯的Bean工厂,也就是常说的IOC容器的顶层定义,各种IOC容器是在其基础上为了满足不同需求而扩展的,包括经常使用的ApplicationContext。

  • BeanFactory 和 ApplicationContext的区别

    1、BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;

    2、BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;ApplicationContext是应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;例如: 国际化(MessageSource)、访问资源,如URL和文件(ResourceLoader)
    、事件机制(ApplicationEventPublisher)、AOP(拦截器)。

    3、 BeanFactory是Spring框架的基础设施,面向Spring本身;而ApplicationContext面向使用Spring的开发者,相比BeanFactory提供了更多面向实际应用的功能,几乎所有场合都可以直接使用ApplicationContext而不是底层的BeanFactory。

  • BeanFacotry 和 FactoryBean的区别

    BeanFacotry:以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

    FactoryBean:以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

    对BeanFacotry的介绍已经很详细了,我们来看下FactoryBean。

    这是个特殊的 Bean 他是个工厂 Bean,可以产生 Bean 的 Bean,一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。

    配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

    FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。

    第三方框架要继承进Spring,往往就是通过实现FactoryBean来集成的。比如MyBatis的SqlSessionFactoryBean、RedisRepositoryFactoryBean、EhCacheManagerFactoryBean等等。

  • Spring如何解决循环依赖的问题

    首先,我们要说明下结论:
    1、构造器循环依赖 和 prototype 范围的依赖处理 是无法解决的。
    2、setter 循环依赖 是通过三层缓存解决的循环依赖。

    在DefaultListableBeanFactory中有三个map,单例模式的 Bean 保存在如下的数据结构中:

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    

    这三个缓存的区别如下:

    • singletonObjects,单例缓存,存储已经实例化完成的单例。
    • singletonFactories,生产单例的工厂的缓存,存储工厂。
    • earlySingletonObjects,提前暴露的单例缓存,这时候的单例刚刚创建完,但还会注入依赖。

    检测循环依赖的过程如下:

    • A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B
    • B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!
    • 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
    • B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
    • 然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面
    • 如此一来便解决了循环依赖的问题

    一句话:先让最底层对象完成初始化,通过三级缓存与二级缓存提前曝光创建中的 Bean,让其他 Bean 率先完成初始化。

AliasRegistry

AliasRegistry 用于管理别名的公共接口,定义对别名的简单增删等操作。用作超级接口。

AliasRegistry 子接口(扩展接口): BeanDefinitionRegistry:BeanDefinition的注册表接口,使BeanDefinition的注册表接口具有别名管理的功能。

AliasRegistry 子类: SimpleBeanDefinitionRegistry: BeanDefinitionRegistry的简单实现。

	public interface AliasRegistry {
	
	    /**
	     * 注册表中给name注册一个别名alias
	     */
	    void registerAlias(String name, String alias);
	
	    /**
	     * 移除注册表中的别名alias
	     */
	    void removeAlias(String alias);
	
	    /**
	     * 校验注册表中是否存在别名name
	     */
	    boolean isAlias(String name);
	
	    /**
	     * 在注册表中获取给定那么的所有别名信息
	     */
	    String[] getAliases(String name);
	}

SimpleAliasRegistry 为AliasRegistry的默认实现,内部使用ConcurrentHashMap作为内存注册表,存储name-alias的映射关系,同时name-alias可以循环引用如: a->b ,c->a ,d->c。

	public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
	
	    // name-BeanDefinition 映射关系表,ConcurrentHashMap实现的内存注册表,用于存储BeanDefinition
	    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
	
	
	    /**
	     * 注册表中注册 name-BeanDefinition
	     */
	    @Override
	    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
	            throws BeanDefinitionStoreException {
	        this.beanDefinitionMap.put(beanName, beanDefinition);
	    }
	
	    /**
	     * 移除注册表中的name-BeanDefinition
	     */
	    @Override
	    public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
	        if (this.beanDefinitionMap.remove(beanName) == null) {
	            throw new NoSuchBeanDefinitionException(beanName);
	        }
	    }
	
	    /**
	     * 获取注册表中的name-BeanDefinition
	     */
	    @Override
	    public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
	        BeanDefinition bd = this.beanDefinitionMap.get(beanName);
	        if (bd == null) {
	            throw new NoSuchBeanDefinitionException(beanName);
	        }
	        return bd;
	    }
	    
	    /**
	     * 判断注册表中是否包含beanName的key
	     */
	    @Override
	    public boolean containsBeanDefinition(String beanName) {
	        return this.beanDefinitionMap.containsKey(beanName);
	    }
	
	    /**
	     * 获取注册表beanName集合
	     */
	    @Override
	    public String[] getBeanDefinitionNames() {
	        return StringUtils.toStringArray(this.beanDefinitionMap.keySet());
	    }
	    /**
	     * 获取注册表大小
	     */
	    @Override
	    public int getBeanDefinitionCount() {
	        return this.beanDefinitionMap.size();
	    }
	
	    /**
	     * 判断beanName是否被使用,在BeanDefinition注册表中获取别名注册表中
	     */
	    @Override
	    public boolean isBeanNameInUse(String beanName) {
	        return isAlias(beanName) || containsBeanDefinition(beanName);
	    }
	
	}

SimpleBeanDefinitionRegistry 为BeanDefinitionRegistry的默认实现,同时继承AliasRegistry的默认实现SimpleAliasRegistry,所以它总共维护了两个注册表aliasMap(别名注册表)与beanDefinitionMap(bean描述注册表)。

DefaultListableBeanFactory

DefaultListableBeanFactory是整个Bean加载的核心部分,是Spring注册及加载Bean的默认实现。下面列出了DefaultListableBeanFactory源代码中两个重要的属性:

    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

    /** List of bean definition names, in registration order */
    private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);

其中,bean的定义被解析成 BeanDefinition,同时解析得到 beanName,将beanName和BeanDefinition存储到beanDefinitionMap中,同时会将beanName存储到beanDefinitionNames中。

BeanDefinition装载前奏曲

上面已经提到了BeanDefinition保存的位置和作用,那么它是如何被装载的呢?我们先看下如果是用XML方式注入的Bean。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationgContext.xml")为入口,进入源码内部,ClassPathXmlApplicationContext类图如下所示。
在这里插入图片描述
ClassPathXmlApplicationContext有多个构造方法,跟踪代码可以发现,最终使用的是下面这个方法。

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

方法的参数很容易理解,configLocations指Spring的xml配置文件;refresh指是否需要刷新,这个refresh决定了是否进行bean解析、注册及实例化;parent指父ApplicationContext。

setConfigLocations方法就是设置框架要加载的资源文件的位置。进入refresh方法,这个方法继承自AbstractApplicationContext,所以具体实现在AbstractApplicationContext类中,具体代码如下。

	@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //容器预先准备,记录容器启动时间和标记
            prepareRefresh();

            //创建bean工厂,里面实现了BeanDefinition的装载
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            //配置bean工厂的上下文信息,如类装载器等
            prepareBeanFactory(beanFactory);

            try {
                //在BeanDefinition被装载后,提供一个修改BeanFactory的入口
                postProcessBeanFactory(beanFactory);

                //在bean初始化之前,提供对BeanDefinition修改入口,PropertyPlaceholderConfigurer 在这里被调用
                invokeBeanFactoryPostProcessors(beanFactory);

                //注册各种 BeanPostProcessors,用于在bean被初始化时进行拦截,进行额外初始化操作
                registerBeanPostProcessors(beanFactory);

                //初始化MessageSource
                initMessageSource();

                 //初始化上下文事件广播
                initApplicationEventMulticaster();

                //这是一个模板方法
                onRefresh();

                //注册监听器
                registerListeners();

               //初始化所有未初始化的非懒加载的单例Bean
                finishBeanFactoryInitialization(beanFactory);

                //发布事件通知
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

这个方法里面就是IOC容器初始化的大致步骤了。上面步骤的第二步完成了BeanDefinition的装载,进入obtainFreshBeanFactory方法,这个方法的具体实现也在AbstractApplicationContext类中,代码如下所示。

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

这里面主要关注refreshBeanFactory方法,这个方法在AbstractApplicationContext类中并未实现,具体实现在子类AbstractRefreshableApplicationContext中,代码如下。

	@Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

这个方法使用了final修饰,也就是不能被重写了。首先检查BeanFactory是否已经存在,如果存在则销毁并关闭,然后新建一个 BeanFactory,其实就是一个DefaultListableBeanFactory。然后进行BeanFactory的属性设置,设置是否允许重写BeanDefinition、是否允许循环引用,接着loadBeanDefinitions方法就是BeanDefinition载入的入口了,这个方法在AbstractRefreshableApplicationContext本类中并未实现,具体在其子类中实现,根据用途不同有多个实现子类。

进入AbstractXmlApplicationContext类的loadBeanDefinitions方法,代码如下所示:

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

关注其中的最后一行的loadBeanDefinitions方法,具体实现也在AbstractXmlApplicationContext类中,代码如下所示。

	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

这个方法里就是根据给定的xml配置文件进行后续的解析及装载。继续跟踪代码,进入reader.loadBeanDefinitions(configLocations),一直查看loadBeanDefinitions的方法,直到 AbstractBeanDefinitionReader#loadBeanDefinitions 方法。

    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }

关注 counter += loadBeanDefinitions(resource) 这行代码,loadBeanDefinitions 方法是个接口。接口具体实现:
在这里插入图片描述
XmlBeanDefinitionReader用于从xml文件中读取Bean的定义。接下来的代码有很多跳转,大部分都是不重要的,直接追踪到重要代码,代码如下。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

这里只要关注doLoadBeanDefinitions方法,进入其实现,代码如下。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

直接看try块中的代码,可以看到,Spring将xml配置文件转成Document,这里使用了SAX对XML的解析,至于里面是如何解析可以后续针对性的研究。接着进入registerBeanDefinitions方法,后续又有很多代码的跳转,先不一一关注,直接进入重要代码,代码如下。

DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            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)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

可以看到 就是对Document中元素、节点的不断解析。这里的解析分成了两条路线,一个是默认标签的解析,如Spring自己定义的标签;一个是对自定义标签的解析,如自定义的标签。这里先关注默认标签的解析,对于自定义标签的解析可以后续分析,进入parseDefaultElement方法,代码如下。

	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

代码中分别对import、alias、bean、beans标签进行解析,最终将解析得到的BeanDefinition和beanName存入DefaultListableBeanFactory中的beanDefinitionMap中。注册过程在 BeanDefinitionReaderUtils#registerBeanDefinition方法中,代码如下:

	public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        
        // BeanDefinition和beanName存入DefaultListableBeanFactory中的beanDefinitionMap中
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

bean初始化

前面提到IOC核心流程中,对于非延迟单例bean的初始化在finishBeanFactoryInitialization(beanFactory)中完成。进入这个方法,代码如下。

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(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return 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();
    }

关注最后一行代码,beanFactory.preInstantiateSingletons()完成初始化所有非延迟的单例bean,进入这个方法的具体实现,代码如下。

    @Override
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }

        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            
            //只对 单例的bean 进行处理
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }

        // Trigger post-initialization callback for all applicable beans...
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        @Override
                        public Object run() {
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                else {
                    smartSingleton.afterSingletonsInstantiated();
                }
            }
        }
    }

从上面的代码中看到,只会对非延迟单例bean进行初始化,scope为其它值的bean会在使用到的时候进行初始化,如prototype。这里关注getBean方法,这个方法看着很眼熟,其实就是《深入理解Spring系列之一:开篇》示例代码中用到的getBean,Spring对这个方法做了重复使用。getBean方法的具体实现在doGetBean方法中,这个方法的代码很长就不贴代码了。在doGetBean中,首先会初始化其依赖的bean,然后进行自身的初始化,这个方法里关注如下的代码段。

                // Create bean instance.
                if (mbd.isSingleton()) {//单例的bean 初始化
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                // 进行 doCreateBean 方法
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                // Explicitly remove instance from singleton cache: It might have been put there
                                // eagerly by the creation process, to allow for circular reference resolution.
                                // Also remove any beans that received a temporary reference to the bean.
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                else if (mbd.isPrototype()) { //多例的bean 初始化
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

追踪代码createBean(beanName, mbd, args)进入doCreateBean方法中,在这个方法中进行bean实例的创建、属性填充、将bean实例加入单例bean实例的缓存中。

doCreateBean方法中有如下代码段。

	if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
    }

createBeanInstance方法里完成bean实例的创建,具体过程可继续追踪代码查看,其实就是使用反射进行实例对象的创建。

web应用自动装配Spring配置

在web应用中使用Spring,需要在web.xml中添加如下配置

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:applicationContext.xml;
        </param-value>
    </context-param>

先了解一下ServletContext和ServletContextListener。ServletContext定义了一些方法方便Servlet和Servlet容器进行通讯,在一个web应用中所有的Servlet都公用一个ServletContext,Spring在和web应用结合使用的时候,是将Spring的容器存到ServletContext中的,通俗的说就是将一个ApplicationContext存储到ServletContext的一个Map属性中;

而ServletContextListener用于监听ServletContext一些事件。分析就从ContextLoaderListener开始。在web应用启动读取web.xml时,发现配置了ContextLoaderListener,而ContextLoaderListener实现了ServletContextListener接口,因此会执行ContextLoaderListener类中的contextInitialized方法,方法的具体代码如下。

    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }

在initWebApplicationContext 方法,将执行如下代码:

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

可以看到,这里将初始化后的context存到了servletContext中,具体的就是存到了一个Map变量中,key值就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个常量。

使用Spring的 WebApplicationContextUtils 工具类获取这个 WebApplicationContext 方式如下。

WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());

常用扩展接口

Spring的单例对象的初始化主要分为三步:

在这里插入图片描述

1、createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
2、populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
3、initializeBean:调用spring xml中的init 方法。

各种接口方法分类:

Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:

1、Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法
2、Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法
3、容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
4、工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。

Spring不仅提供了一个进行快速开发的基础框架,而且还提供了很多可扩展的接口,用于满足一些额外的开发需求,本篇将对常用的可扩展接口进行归纳总结。

  1. InitializingBean接口
    InitializingBean接口中只有一个afterPropertiesSet方法,从方法的名称上很容易理解,这个方法是在Bean的属性都设置值后被调用,用于完成一些初始化工作。当然,在Spring的配置文件中init-method的配置也是在Bean的属性都设置值后被调用,用于完成一些初始化工作,不过在执行顺序上,接口的方法先于配置。值得注意的是,这两种方式都是用于完成一些初始化工作,所以相应的方法中不要编写一些复杂且执行时间很长的逻辑。

  2. DisposableBean接口
    DisposableBean接口中只有一个destroy方法,该方法会在Bean被销毁、生命周期结束之前被调用,用于做一些销毁的收尾工作。同样,在Spring的配置文件中destroy-method配置也完成同样的工作,不过在执行顺序上,接口的方法先于配置。

  3. ApplicationContextAware接口
    ApplicationContextAware中只有一个setApplicationContext方法。实现了ApplicationContextAware接口的类,可以在该Bean被加载的过程中获取Spring的应用上下文ApplicationContext,通过ApplicationContext可以获取Spring容器内的很多信息。

  4. BeanFactoryAware接口
    BeanFactoryAware接口中只有一个setBeanFactory方法。实现了BeanFactoryAware接口的类,可以在该Bean被加载的过程中获取加载该Bean的BeanFactory,同时也可以获取这个BeanFactory中加载的其它Bean。

  5. FactoryBean接口
    FactoryBean接口可以实现Bean实例化的个性定制,让Spring容器加载我们想要的Bean。实现了FactoryBean接口的类,可以通过实现getObject方法,实现加载我们想要的Bean(如:SqlSessionFactoryBean)。

  6. BeanPostProcessor接口
    BeanPostProcessor接口中有两个方法,分别为postProcessBeforeInitialization和postProcessAfterInitialization。实现了BeanPostProcessor接口的类,会在每个Bean初始化(即调用setter)之前和之后,分别调用这个类中的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,实现初始化的逻辑控制。

  7. InstantiationAwareBeanPostProcessor接口
    InstantiationAwareBeanPostProcessor接口中,常用的方法是postProcessBeforeInstantiation和postProcessAfterInstantiation。每个Bean的实例化(即调用构造函数)之前和之后,会分别调用实现了该接口的类中的postProcessBeforeInstantiation和postProcessAfterInstantiation方法。

  8. BeanFactoryPostProcessor接口
    BeanFactoryPostProcessor接口中只有postProcessBeanFactory方法。实现了该接口的类,可以在Bean被创建之前,获取容器中Bean的定义信息,并且可以进行修改。实现类中的postProcessBeanFactory方法只会被执行一次,且先于BeanPostProcessor接口的方法。

总结

最后我们梳理下整个流程:

  1. 首先,通过解析xml或者通过@Component注解获取Bean的定义信息,分装到BeanDefinition中,加入到Map 中。
  2. 容器对外暴露了getBean方法,通过BeanName获取具体的Bean。
  3. 转化bean Name,有可能是这几种情况:1)bean name,可以直接获取到定义 BeanDefinition。2)alias name,别名,需要转化。3)factorybean name, 带 & 前缀,通过它获取 BeanDefinition 的时候需要去除 & 前缀。
  4. 合并 RootBeanDefinition,我们从配置文件读取到的 BeanDefinition 是 GenericBeanDefinition。它并不包含父类的全量信息。RootBeanDefinition则保存着全量信息。
  5. 处理单例模式循环依赖,单例模式下,构造函数的循环依赖无法解决,但设值循环依赖是可以解决的:提前暴露创建中的单例。
  6. 创建实例,获取到完整的 RootBeanDefintion 后,就可以创建Bean 的包装类 BeanWrapper。
  7. 注入属性,实例创建完后开始进行属性的注入,如果涉及到外部依赖的实例,会自动检索并关联到该当前实例。会递归调用 BeanFactory.getBean 来获得。
  8. 初始化开始。
  9. 触发 Aware,如果我们的 Bean 需要容器的一些资源该怎么办?Spring 提供了 Aware 系列接口来解决这个问题。比如有这样的 Aware:1)BeanFactoryAware,用来获取 BeanFactory。2)ApplicationContextAware,用来获取 ApplicationContext。3)ResourceLoaderAware,用来获取 ResourceLoaderAware。4)ServletContextAware,用来获取 ServletContext。
  10. 触发 BeanPostProcessor ,在 Bean 的初始化前或者初始化后,我们如果需要进行一些增强操作怎么办?这些增强操作比如打日志、做校验、属性修改、耗时检测等等。Spring 框架提供了 BeanPostProcessor 来达成这个目标。
  11. 触发自定义 init,实现 InitializingBean。
  12. 类型转换,Bean 已经加载完毕,属性也填充好了,初始化也完成了。在返回给调用者之前,还留有一个机会对 Bean 实例进行类型的转换。
发布了44 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/hxyascx/article/details/103703912