Spring源码分析之IoC容器初始化

栏目: 编程工具 · 发布时间: 7年前

内容简介:作为一个java程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring,所以这次也要做个深入了解!这次就来看看Spring是怎么注:阅读本文时一定要在IDE进行跳转

作为一个 java 程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring,所以这次也要做个深入了解!这次就来看看Spring是怎么 初始化IoC容器的

Spring源码分析之IoC容器初始化

注:阅读本文时一定要在IDE进行跳转

我们都是知道Spring为企业级应用提供了丰富的功能,而这些功能底层都依赖于底层最核心的两种特性IOC和AOP。

IOC实现里主要包括两部分,一个是IOC容器初始,另外一个是依赖注入,由于两部分是相对对立的部分,所以分成不同文章讲解,本篇主要 讲述IOC容器的初始化

一、IoC的概念

控制反转(Inversion of Control,缩写为 IoC ),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的 耦合度 。其中最常见的方式叫做 依赖注入 (Dependency Injection,简称 DI ),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

上面这个概念是来自维基百科,在 Expert Spring MVC and Web FlowExpert One-on-One J2EE without EJB 等书中都也是将依赖注入看作是IoC的一种方式。不过有些地方会把IoC和DI看成一个概念(例如Spring in Action、Spring揭密等书),不过没关系,不影响我们理解就可以。

白话版

A类的正常的运行需要B类

没有IoC之前我们在使用A类的某个方法时,总会依赖于B类的一些功能,这样我们就要去new个B对象,有时还要考虑什么时候销毁,什么时候使用单例模式等等,类少了还好管理,这要是多起来真是再聪明的人怕也是要十个头九个大了, 而且A、B之间的依赖关系使各代码紧密耦合,一旦B类的出现问题,或者某天干脆不用B了用C,那是不是A类里的new B()全得换成new C()?想象都觉得累…

有了IoC之后,对象创建都交给第三方容器处理,A中的B通过注入来完成,B出问题了,或者需要换成C了,只要把注入的B换成C就可以(现实开发中B、C可以实现相同的接口解决~所以啊,Spring是面向接口编程鸭)。

Tips

  1. Expert One-on-One J2EE without EJB 这本书是spring爸爸Rod Johnson写的,进入Spring的BeanFactory类里面看看作者就是他,哈哈!
  2. 浅谈控制反转与依赖注入 :这是我看过最好的一篇对控制反转的解释,强烈推荐!

二、IoC容器初始化

预备内容

本节只讲解IoC容器的初始化,其中包括创建容器和将bean装入到容器中,下面这三件事是该部分的核心:

BeanDefinition的Resource定位

BeanDefinition的载入和解析

BeanDefinition在容器中的注册

因为Spring的IoC容器实现太复杂了,各种类之间的调用很容易就让我们陷入到细节之中,结果就走的太远忘记了为啥要出发了,本文主要将述容器初始化时最主要的三件事。

先了解几个概念:

BeanFactory:这是IOC容器的接口定义,提供了IoC最基本的功能,如果说容器是个汽车工厂,那么这个结构就规定了汽车工厂最基本的功能,能储存零件,能组装汽车。

public interface BeanFactory {
	/**
	 * 使用容器获取bean时,添加转义自符&可以获取到FactoryBean本身而吧是Factory产生的对象
	 */
	String FACTORY_BEAN_PREFIX = "&";
	/**
	 * 通过bean的名字来获取bean
	 */
	Object getBean(String name) throws BeansException;
	/**
	 * 通过bean的类型和类型来获取bean
	 */
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	/**
	 * 通过bean的类型来获取bean
	 */
	<T> T getBean(Class<T> requiredType) throws BeansException;
	/**
	 * 通过名字和参数来获取bean
	 */
	Object getBean(String name, Object... args) throws BeansException;
	/**
	 * 是否包含名字为name的bean
	 */
	boolean containsBean(String name);
	/**
	 * 是否单例
	 */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 是否原型
	 */
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 名字为name的bean是否是targetType类型
	 */
	boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
	/**
	 * 获取名字为name的bean类型
	 */
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 获取名字为name的bean的别名字集合
	 */
	String[] getAliases(String name);

}

ApplicationContext:升级版汽车厂,除了上面的功能,还提供很多人性化的服务,继承了 MessageSource,ResourceLoader,ApplicationEventPublisher等等接口,在BeanFactory 简单IoC容器的基础上添加了许多对高级容器的支持。

Spring源码分析之IoC容器初始化

public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver {
	/**
	 * 返回该上下文的id(unique)
	 */
	String getId();

	/**
	 *  返回上下文所属应用的名字
	 */
	String getApplicationName();

	/**
	 * 返回这个上下文友好的名字
	 */
	String getDisplayName();

	/**
	 * 返回上下文首次加载的时间
	 */
	long getStartupDate();

	/**
	 * 返回父类上下文
	 */
	ApplicationContext getParent();

	/**
	 * 功能性的暴露自动装配的工厂,并不常用
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

这里面的方法也不多,主要的方法都在继承的接口里

BeanDifinition:储存 Spring中 Bean的信息,包括各属性名,类名,是否单例等,抽象了我们对 Bean的定义,是让容器起作用的主要数据类型。对 IOC 容器来说,BeanDefinition 就是对控制反转模式中管理的对象依赖关系的数据抽象。

接下来正式进入IoC容器初始化的分析,以FileSystemXmlApplicationContext为例,下面是FileSystemXmlApplicationContext的继承关系~(这形状,满满的爱啊,哈哈)

Spring源码分析之IoC容器初始化

BeanDefinition的Resource定位

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
	/**
	 * 无参构造
	 */
	public FileSystemXmlApplicationContext() {
	}
	/**
	 * 传入父类上下文
	 */
	public FileSystemXmlApplicationContext(ApplicationContext parent) {
		super(parent);
	}
	/**
	 * 核心构造方法,其他几个都基于本构造方法
	 * configLocations 传入xml配置文件位置集合
	 * refresh 是否自动刷新容器(是refresh方法的调用,初始化上下文的核心方法)
	 * parent 父类上下文
	 * 1.传入配置文件地址
	 * 2.刷新容器
	 */
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

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

	/**
	 * 通过给定的路径在文件系统中定位BeanDefinition并返回一个FileSystemResource
	 * 这个方法是BeanDefinitionReader的loadBeanDefinition中被调用,
	 * loadBeanDefinition采用了模板模式,具体实现在不同的子类中(默认是类路径)
	 */
	@Override
	protected Resource getResourceByPath(String path) {
		if (path != null && path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}

}

看以看出,本类对所有configLocation都进行了处理,使所有以xml形式存在的BeanDefinition都得到了处理,其中这个refresh就最最关键点方法,接下来对refresh进行解析。

refresh是在AbstractApplicationContext中实现,理解了refresh方法,基本就理解了IoC初始化的全过程了。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 刷新前准备活动
        prepareRefresh();

        // 关键方法构建beanFactory——>接下来会详解本方法
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 为在这个上下文中使用beanFactory做准备
        prepareBeanFactory(beanFactory);

        try {
            // 设置后置处理器
            postProcessBeanFactory(beanFactory);

            // 调用bean的后置处理器,这些处理器在上下文中被注册为bean的形式
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            // 注册拦截bean创建的处理器
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            // 为上下文初始化消息源,国际化功能
            initMessageSource();

            // Initialize event multicaster for this context.
            // 初始化上下文的时间机制
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            // 初始化其他特殊bean在特殊上下文子类中
            onRefresh();

            // Check for listener beans and register them.
            // 检查监听的bean,并将他们注册到容器中
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            // 初始化所有的非懒加载单件
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            // 发布相关事件,结束refresh
            finishRefresh();
        }

        catch (BeansException ex) {
            // Destroy already created singletons to avoid dangling resources.
            // 出现异常销毁beans
            destroyBeans();

            // Reset 'active' flag.
            // 这个active在上面的prepare中被设置为了true
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }
    }
}

接下来我们详细来看一下容器的构建过程,在类AbstractRefreshableApplicationContext中

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 刷新beanfactory,这个方法很重要,就是它构建的bean,【继续往里走】
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}
@Override
protected final void refreshBeanFactory() throws BeansException {
    // 如果已经存在beanfactory那就销毁掉bean并把工厂关了,避免对接下来的初始化造成影响
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 这里创建了一个DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        // 设置唯一id,用于序列化
        beanFactory.setSerializationId(getId());
        // 自定义bean工厂
        customizeBeanFactory(beanFactory);
        // 向工厂中加载BeanDefinition,这个很重要,【继续往里走】
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

loadBeanDefinitions在AbstractRefreshableApplicationContext中是个抽象方法,我直接找到了在子类AbstractXmlApplicationContext(其实有三个实现类,但是我们现在研究的是FileSystemXmlApplicationContext)中的实现。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 看这名字,显然就是用这家伙对来将xml配置文件中的信息读取放到容器里的.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    //使用这个上下文的环境资源对这个reader进行配置
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // 允许子类个性化对这个reader初始化
    initBeanDefinitionReader(beanDefinitionReader);
    // 然后开始真正的加载BeanDefinition,【继续往里走】
    loadBeanDefinitions(beanDefinitionReader);
}

这次是跳转到AbstractXmlApplicationContext里面,继续阅读

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    // 获取配置资源~~不过直接返回了一个null,不过有子类进行重写的(如果看过HashMap源码,那肯定记得里面有几个空实现是给LinkedHashMap用的,这里同样的道理)
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    // 这个是类AbstractRefreshableConfigApplicationContext中的方法(跳来跳去脑壳都大了。。。)
    // FileSystemXmlApplicationContext在refresh前就设置了
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        // 终于开始解析了,【继续往里走】
        reader.loadBeanDefinitions(configLocations);
    }
}

AbstractBeanDefinitionReade中的方法,就是在这里进行加载的

@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int counter = 0;
    for (String location : locations) {
        // ....【继续往里面走】
        counter += loadBeanDefinitions(location);
    }
    return counter;
}

还在本类里~不用跳了

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    // 获取资源加载器
    ResourceLoader resourceLoader = getResourceLoader();
    // 空就抛异常
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }
	// 这个是用来解析classpath*:这种的路径,可以是多个配置文件
    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            //到这里getResource【完成了具体的定位】
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            // 开始载入BeanDefinition
            int loadCount = loadBeanDefinitions(resources);
            if (actualResources != null) {
                for (Resource resource : resources) {
                    actualResources.add(resource);
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
            }
            return loadCount;
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                "Could not resolve bean definition resource pattern [" + location + "]", ex);
        }
    }
    else {
        // 到这里getResource,resource接口中封装了很多与I/O相关的操作
        // 至此【完成了具体的定位】
        Resource resource = resourceLoader.getResource(location);
        // 开始载入BeanDefinition【继续往里跳】
        int loadCount = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
        }
        return loadCount;
    }
}

refresh方法完成IoC的整个初始化,其中 refreshBeanFactory()方法非常的重要,本小节讲到定位,下一小节开始讲解BeanDefinition解析与加载。

BeanDefinition的载入和解析

对于IoC容器来说,这个载入过程相当于把xml中的BeanDefinition转换成一个Spring内部的数据结构的过程。IoC容器对Bean的管理和依赖注入功能是通过对其持有的BeanDefinition进行各种相关操作来实现的。这些BeanDefinition是通过一个HashMap来实现的。

Spring源码分析之IoC容器初始化

承接上文,loadBeanDefinitions()是对BeanDefinition载入和解析的核心方法。具体实现在XMLBeanDefinitionReader里面。

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;
}
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    // 对Resource进行包装,提供通过不同编码方式读取资源文件,【继续往里面跳】
    return loadBeanDefinitions(new EncodedResource(resource));
}
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());
    }
	// 获取已经加载的Resource
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    // 解决重复依赖问题,encodedResource的equals方法已经被重写
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    // 这里获取IO准备读取XML中的BeanDefinition
    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();
        }
    }
}

这个是在XMLBeanDefinitionReader中实现(没有进其他类)

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
    try {
        // 这里取得XML的document对象,解析由documentLoader完成,感兴趣看以进去看看步骤
        // 虽然已经对xml进行解析但是并没有按照bean的规则,所以需要继续解析
        Document doc = doLoadDocument(inputSource, resource);
        // 这里启动的是对beanDefinition解析的详细过程,很重要的方法,【继续往里面跳】
        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);
    }
}

仍然在本类中

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 创建BeanDefinitionDocumentReader对document进行解析
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    documentReader.setEnvironment(this.getEnvironment());
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 具体的解析过程,【继续往里面跳】
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

进入DefaultBeanDefinitionDocumentReader类中,在文档元素中获取根元素,并继续调用doRegisterBeanDefinition进行注册。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    // 【继续往里面跳】
    doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
    String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    if (StringUtils.hasText(profileSpec)) {
        Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        if (!this.environment.acceptsProfiles(specifiedProfiles)) {
            return;
        }
    }
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(this.readerContext, root, parent);

    preProcessXml(root);
    //  对BeanDefinition进行解析,该方法的核心逻辑,【继续往里面跳】
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}
// 不难看出,这是对xml的解析过程
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);
    }
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // import标签
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    // 别名
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    // bean标签
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        // 对bean标签进行解析,【继续往里面跳】
        processBeanDefinition(ele, delegate);
    }
    // beans标签
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 这个是BeanDefinitionHolder里装有BeanDefinition对象和beanname,别名集合等信息
    // parseBeanDefinitionElement()这个方法将xml中bean的定义进行解析,有兴趣可以进去深入了解
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 向IoC容器注册解析到的BeanDefinition,【继续往里面跳】
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                                     bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

至此,XML中的BeanDefinition的解析和载入全部完成,接下来进入bean的注册部分。

BeanDefinition在IoC容器中的注册

经过定位和载入后,BeanDefinition已经在IoC建立起相应的数据结构,为了更友好的使用这些BeanDefinition,需要在IoC容器中将这些BeanDefinition进行注册。

该方法在BeanDefinitionReaderUtils类中

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

    String beanName = definitionHolder.getBeanName();
    // 这个很明显是将BeanDefinition注册的方法,【继续往里面跳】
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

跳转到DefaultListableBeanFactory类中,前面创建工厂时用的就是这个工厂

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }
	// 为了保证数据一致性,注册时加个synchronized线程锁
    synchronized (this.beanDefinitionMap) {
        // 检查在IoC容器中是否有同名bean,有同名的还不让覆盖的就是抛异常
        BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!this.allowBeanDefinitionOverriding) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                       "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                                                       "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +" with a framework-generated bean definition ': replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
        }
        else {
            this.beanDefinitionNames.add(beanName);
            this.frozenBeanDefinitionNames = null;
        }
        // 把BeanDefinition装到如到beanDefinitionMap中
        // 【至此Spring IoC容器初始化完成~】
        // beanDeinitionMap是初始长度64的ConcurrentHashMap
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    resetBeanDefinition(beanName);
}

到这里,注册完成。我们创建bean工厂,将BeanDefinition注册到了IoC容器持有的Map中。这些信息是控制反转的基础。

三、小结

本文开始简略解释IoC的概念,然后从FileSystemXmlApplicationContext着手,根据源码一步一步讲述从bean工厂创建到BeanDefinition在IoC中注册核心逻辑。Spring源码确实细节太多,在阅读源码过程中,一定要抓住核心逻辑。

本文是博主在学习Spring源码过程中对IoC的总结,希望对想要阅读源码但不知从何下手的同学有所帮助!如有错误希望大家指正。

本文首发于cdream个人博客

欢迎转载,转载请注明出处!

本文参考:

  1. Spring揭秘,王福强
  2. Spring技术内幕,计文柯
  3. Spring源码深度分析,郝佳
  4. 深入理解-Spring-之源码剖析IOC
  5. 浅谈控制反转与依赖注入

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

Larry Ullman / Peachpit Press / 2004-02-02 / USD 29.99

So you know HTML, even JavaScript, but the idea of learning an actual programming language like PHP terrifies you? Well, stop quaking and get going with this easy task-based guide! Aimed at beginning ......一起来看看 《PHP for the World Wide Web, Second Edition (Visual QuickStart Gu》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具