经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Spring源码分析之IoC容器初始化
来源:cnblogs  作者:cdream  时间:2018/12/10 9:50:33  对本文有异议

本文首发于cdream个人博客(点击获得更加阅读体验)

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

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

image-20181204223850227

注:阅读本文时一定要在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最基本的功能,如果说容器是个汽车工厂,那么这个结构就规定了汽车工厂最基本的功能,能储存零件,能组装汽车。

  1. public interface BeanFactory {
  2.     /**
  3.      * 使用容器获取bean时,添加转义自符&可以获取到FactoryBean本身而吧是Factory产生的对象
  4.      */
  5.     String FACTORY_BEAN_PREFIX = "&";
  6.     /**
  7.      * 通过bean的名字来获取bean
  8.      */
  9.     Object getBean(String name) throws BeansException;
  10.     /**
  11.      * 通过bean的类型和类型来获取bean
  12.      */
  13.     <T> T getBean(String name, Class<T> requiredType) throws BeansException;
  14.     /**
  15.      * 通过bean的类型来获取bean
  16.      */
  17.     <T> T getBean(Class<T> requiredType) throws BeansException;
  18.     /**
  19.      * 通过名字和参数来获取bean
  20.      */
  21.     Object getBean(String name, Object... args) throws BeansException;
  22.     /**
  23.      * 是否包含名字为name的bean
  24.      */
  25.     boolean containsBean(String name);
  26.     /**
  27.      * 是否单例
  28.      */
  29.     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
  30.     /**
  31.      * 是否原型
  32.      */
  33.     boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
  34.     /**
  35.      * 名字为name的bean是否是targetType类型
  36.      */
  37.     boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
  38.     /**
  39.      * 获取名字为name的bean类型
  40.      */
  41.     Class<?> getType(String name) throws NoSuchBeanDefinitionException;
  42.     /**
  43.      * 获取名字为name的bean的别名字集合
  44.      */
  45.     String[] getAliases(String name);
  46.  
  47. }

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

image-20181201171729612

  1. public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver {
  2.     /**
  3.      * 返回该上下文的id(unique)
  4.      */
  5.     String getId();
  6.  
  7.     /**
  8.      *  返回上下文所属应用的名字
  9.      */
  10.     String getApplicationName();
  11.  
  12.     /**
  13.      * 返回这个上下文友好的名字
  14.      */
  15.     String getDisplayName();
  16.  
  17.     /**
  18.      * 返回上下文首次加载的时间
  19.      */
  20.     long getStartupDate();
  21.  
  22.     /**
  23.      * 返回父类上下文
  24.      */
  25.     ApplicationContext getParent();
  26.  
  27.     /**
  28.      * 功能性的暴露自动装配的工厂,并不常用
  29.      */
  30.     AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
  31. }

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

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

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

image-20181203195616507

BeanDefinition的Resource定位

  1. public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
  2.     /**
  3.      * 无参构造
  4.      */
  5.     public FileSystemXmlApplicationContext() {
  6.     }
  7.     /**
  8.      * 传入父类上下文
  9.      */
  10.     public FileSystemXmlApplicationContext(ApplicationContext parent) {
  11.         super(parent);
  12.     }
  13.     /**
  14.      * 核心构造方法,其他几个都基于本构造方法
  15.      * configLocations 传入xml配置文件位置集合
  16.      * refresh 是否自动刷新容器(是refresh方法的调用,初始化上下文的核心方法)
  17.      * parent 父类上下文
  18.      * 1.传入配置文件地址
  19.      * 2.刷新容器
  20.      */
  21.     public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
  22.             throws BeansException {
  23.  
  24.         super(parent);
  25.         setConfigLocations(configLocations);
  26.         if (refresh) {
  27.             refresh();
  28.         }
  29.     }
  30.  
  31.     /**
  32.      * 通过给定的路径在文件系统中定位BeanDefinition并返回一个FileSystemResource
  33.      * 这个方法是BeanDefinitionReader的loadBeanDefinition中被调用,
  34.      * loadBeanDefinition采用了模板模式,具体实现在不同的子类中(默认是类路径)
  35.      */
  36.     @Override
  37.     protected Resource getResourceByPath(String path) {
  38.         if (path != null && path.startsWith("/")) {
  39.             path = path.substring(1);
  40.         }
  41.         return new FileSystemResource(path);
  42.     }
  43.  
  44. }

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

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

  1. public void refresh() throws BeansException, IllegalStateException {
  2.     synchronized (this.startupShutdownMonitor) {
  3.         // 刷新前准备活动
  4.         prepareRefresh();
  5.  
  6.         // 关键方法构建beanFactory——>接下来会详解本方法
  7.         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  8.  
  9.         // 为在这个上下文中使用beanFactory做准备
  10.         prepareBeanFactory(beanFactory);
  11.  
  12.         try {
  13.             // 设置后置处理器
  14.             postProcessBeanFactory(beanFactory);
  15.  
  16.             // 调用bean的后置处理器,这些处理器在上下文中被注册为bean的形式
  17.             // Invoke factory processors registered as beans in the context.
  18.             invokeBeanFactoryPostProcessors(beanFactory);
  19.  
  20.             // Register bean processors that intercept bean creation.
  21.             // 注册拦截bean创建的处理器
  22.             registerBeanPostProcessors(beanFactory);
  23.  
  24.             // Initialize message source for this context.
  25.             // 为上下文初始化消息源,国际化功能
  26.             initMessageSource();
  27.  
  28.             // Initialize event multicaster for this context.
  29.             // 初始化上下文的时间机制
  30.             initApplicationEventMulticaster();
  31.  
  32.             // Initialize other special beans in specific context subclasses.
  33.             // 初始化其他特殊bean在特殊上下文子类中
  34.             onRefresh();
  35.  
  36.             // Check for listener beans and register them.
  37.             // 检查监听的bean,并将他们注册到容器中
  38.             registerListeners();
  39.  
  40.             // Instantiate all remaining (non-lazy-init) singletons.
  41.             // 初始化所有的非懒加载单件
  42.             finishBeanFactoryInitialization(beanFactory);
  43.  
  44.             // Last step: publish corresponding event.
  45.             // 发布相关事件,结束refresh
  46.             finishRefresh();
  47.         }
  48.  
  49.         catch (BeansException ex) {
  50.             // Destroy already created singletons to avoid dangling resources.
  51.             // 出现异常销毁beans
  52.             destroyBeans();
  53.  
  54.             // Reset 'active' flag.
  55.             // 这个active在上面的prepare中被设置为了true
  56.             cancelRefresh(ex);
  57.  
  58.             // Propagate exception to caller.
  59.             throw ex;
  60.         }
  61.     }
  62. }

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

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

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

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  2.     // 看这名字,显然就是用这家伙对来将xml配置文件中的信息读取放到容器里的.
  3.     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  4.  
  5.     //使用这个上下文的环境资源对这个reader进行配置
  6.     beanDefinitionReader.setEnvironment(this.getEnvironment());
  7.     beanDefinitionReader.setResourceLoader(this);
  8.     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  9.  
  10.     // 允许子类个性化对这个reader初始化
  11.     initBeanDefinitionReader(beanDefinitionReader);
  12.     // 然后开始真正的加载BeanDefinition,【继续往里走】
  13.     loadBeanDefinitions(beanDefinitionReader);
  14. }

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

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

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

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

还在本类里~不用跳了

  1. public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
  2.     // 获取资源加载器
  3.     ResourceLoader resourceLoader = getResourceLoader();
  4.     // 空就抛异常
  5.     if (resourceLoader == null) {
  6.         throw new BeanDefinitionStoreException(
  7.             "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
  8.     }
  9.     // 这个是用来解析classpath*:这种的路径,可以是多个配置文件
  10.     if (resourceLoader instanceof ResourcePatternResolver) {
  11.         // Resource pattern matching available.
  12.         try {
  13.             //到这里getResource【完成了具体的定位】
  14.             Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  15.             // 开始载入BeanDefinition
  16.             int loadCount = loadBeanDefinitions(resources);
  17.             if (actualResources != null) {
  18.                 for (Resource resource : resources) {
  19.                     actualResources.add(resource);
  20.                 }
  21.             }
  22.             if (logger.isDebugEnabled()) {
  23.                 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
  24.             }
  25.             return loadCount;
  26.         }
  27.         catch (IOException ex) {
  28.             throw new BeanDefinitionStoreException(
  29.                 "Could not resolve bean definition resource pattern [" + location + "]", ex);
  30.         }
  31.     }
  32.     else {
  33.         // 到这里getResource,resource接口中封装了很多与I/O相关的操作
  34.         // 至此【完成了具体的定位】
  35.         Resource resource = resourceLoader.getResource(location);
  36.         // 开始载入BeanDefinition【继续往里跳】
  37.         int loadCount = loadBeanDefinitions(resource);
  38.         if (actualResources != null) {
  39.             actualResources.add(resource);
  40.         }
  41.         if (logger.isDebugEnabled()) {
  42.             logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
  43.         }
  44.         return loadCount;
  45.     }
  46. }

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

BeanDefinition的载入和解析

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

image-20181204074701504

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

  1. public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
  2.     Assert.notNull(resources, "Resource array must not be null");
  3.     int counter = 0;
  4.     for (Resource resource : resources) {
  5.         // 这里是循环加载,【继续往里面跳】
  6.         counter += loadBeanDefinitions(resource);
  7.     }
  8.     return counter;
  9. }
  1. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  2.     // 对Resource进行包装,提供通过不同编码方式读取资源文件,【继续往里面跳】
  3.     return loadBeanDefinitions(new EncodedResource(resource));
  4. }
  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2.     Assert.notNull(encodedResource, "EncodedResource must not be null");
  3.     if (logger.isInfoEnabled()) {
  4.         logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  5.     }
  6.     // 获取已经加载的Resource
  7.     Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  8.     if (currentResources == null) {
  9.         currentResources = new HashSet<EncodedResource>(4);
  10.         this.resourcesCurrentlyBeingLoaded.set(currentResources);
  11.     }
  12.     // 解决重复依赖问题,encodedResource的equals方法已经被重写
  13.     if (!currentResources.add(encodedResource)) {
  14.         throw new BeanDefinitionStoreException(
  15.             "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  16.     }
  17.     // 这里获取IO准备读取XML中的BeanDefinition
  18.     try {
  19.         InputStream inputStream = encodedResource.getResource().getInputStream();
  20.         try {
  21.             InputSource inputSource = new InputSource(inputStream);
  22.             if (encodedResource.getEncoding() != null) {
  23.                 inputSource.setEncoding(encodedResource.getEncoding());
  24.             }
  25.             // 【继续往里面跳】
  26.             return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  27.         }
  28.         finally {
  29.             inputStream.close();
  30.         }
  31.     }
  32.     catch (IOException ex) {
  33.         throw new BeanDefinitionStoreException(
  34.             "IOException parsing XML document from " + encodedResource.getResource(), ex);
  35.     }
  36.     finally {
  37.         currentResources.remove(encodedResource);
  38.         if (currentResources.isEmpty()) {
  39.             this.resourcesCurrentlyBeingLoaded.remove();
  40.         }
  41.     }
  42. }

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

  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2.     throws BeanDefinitionStoreException {
  3.     try {
  4.         // 这里取得XML的document对象,解析由documentLoader完成,感兴趣看以进去看看步骤
  5.         // 虽然已经对xml进行解析但是并没有按照bean的规则,所以需要继续解析
  6.         Document doc = doLoadDocument(inputSource, resource);
  7.         // 这里启动的是对beanDefinition解析的详细过程,很重要的方法,【继续往里面跳】
  8.         return registerBeanDefinitions(doc, resource);
  9.     }
  10.     catch (BeanDefinitionStoreException ex) {
  11.         throw ex;
  12.     }
  13.     catch (SAXParseException ex) {
  14.         throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  15.                                                   "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  16.     }
  17.     catch (SAXException ex) {
  18.         throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  19.                                                   "XML document from " + resource + " is invalid", ex);
  20.     }
  21.     catch (ParserConfigurationException ex) {
  22.         throw new BeanDefinitionStoreException(resource.getDescription(),
  23.                                                "Parser configuration exception parsing XML from " + resource, ex);
  24.     }
  25.     catch (IOException ex) {
  26.         throw new BeanDefinitionStoreException(resource.getDescription(),
  27.                                                "IOException parsing XML document from " + resource, ex);
  28.     }
  29.     catch (Throwable ex) {
  30.         throw new BeanDefinitionStoreException(resource.getDescription(),
  31.                                                "Unexpected exception parsing XML document from " + resource, ex);
  32.     }
  33. }

仍然在本类中

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

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

  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  2.     this.readerContext = readerContext;
  3.     logger.debug("Loading bean definitions");
  4.     Element root = doc.getDocumentElement();
  5.     // 【继续往里面跳】
  6.     doRegisterBeanDefinitions(root);
  7. }
  1. protected void doRegisterBeanDefinitions(Element root) {
  2.     String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  3.     if (StringUtils.hasText(profileSpec)) {
  4.         Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
  5.         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  6.             profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  7.         if (!this.environment.acceptsProfiles(specifiedProfiles)) {
  8.             return;
  9.         }
  10.     }
  11.     BeanDefinitionParserDelegate parent = this.delegate;
  12.     this.delegate = createDelegate(this.readerContext, root, parent);
  13.  
  14.     preProcessXml(root);
  15.     //  对BeanDefinition进行解析,该方法的核心逻辑,【继续往里面跳】
  16.     parseBeanDefinitions(root, this.delegate);
  17.     postProcessXml(root);
  18.  
  19.     this.delegate = parent;
  20. }
  1. // 不难看出,这是对xml的解析过程
  2. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  3.     if (delegate.isDefaultNamespace(root)) {
  4.         NodeList nl = root.getChildNodes();
  5.         for (int i = 0; i < nl.getLength(); i++) {
  6.             Node node = nl.item(i);
  7.             if (node instanceof Element) {
  8.                 Element ele = (Element) node;
  9.                 if (delegate.isDefaultNamespace(ele)) {
  10.                     // 本逻辑为核心逻辑,【继续往里面跳】
  11.                     parseDefaultElement(ele, delegate);
  12.                 }
  13.                 else {
  14.                     delegate.parseCustomElement(ele);
  15.                 }
  16.             }
  17.         }
  18.     }
  19.     else {
  20.         delegate.parseCustomElement(root);
  21.     }
  22. }
  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2.     // import标签
  3.     if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  4.         importBeanDefinitionResource(ele);
  5.     }
  6.     // 别名
  7.     else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  8.         processAliasRegistration(ele);
  9.     }
  10.     // bean标签
  11.     else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  12.         // 对bean标签进行解析,【继续往里面跳】
  13.         processBeanDefinition(ele, delegate);
  14.     }
  15.     // beans标签
  16.     else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  17.         // recurse
  18.         doRegisterBeanDefinitions(ele);
  19.     }
  20. }
  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2.     // 这个是BeanDefinitionHolder里装有BeanDefinition对象和beanname,别名集合等信息
  3.     // parseBeanDefinitionElement()这个方法将xml中bean的定义进行解析,有兴趣可以进去深入了解
  4.     BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  5.     if (bdHolder != null) {
  6.         bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  7.         try {
  8.             // 向IoC容器注册解析到的BeanDefinition,【继续往里面跳】
  9.             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  10.         }
  11.         catch (BeanDefinitionStoreException ex) {
  12.             getReaderContext().error("Failed to register bean definition with name '" +
  13.                                      bdHolder.getBeanName() + "'", ele, ex);
  14.         }
  15.         // Send registration event.
  16.         getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  17.     }
  18. }

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

BeanDefinition在IoC容器中的注册

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

该方法在BeanDefinitionReaderUtils类中

  1. public static void registerBeanDefinition(
  2.     BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  3.     throws BeanDefinitionStoreException {
  4.  
  5.     String beanName = definitionHolder.getBeanName();
  6.     // 这个很明显是将BeanDefinition注册的方法,【继续往里面跳】
  7.     registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  8.  
  9.     // Register aliases for bean name, if any.
  10.     String[] aliases = definitionHolder.getAliases();
  11.     if (aliases != null) {
  12.         for (String aliase : aliases) {
  13.             registry.registerAlias(beanName, aliase);
  14.         }
  15.     }
  16. }

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

  1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  2.     throws BeanDefinitionStoreException {
  3.  
  4.     Assert.hasText(beanName, "Bean name must not be empty");
  5.     Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  6.  
  7.     if (beanDefinition instanceof AbstractBeanDefinition) {
  8.         try {
  9.             ((AbstractBeanDefinition) beanDefinition).validate();
  10.         }
  11.         catch (BeanDefinitionValidationException ex) {
  12.             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  13.                                                    "Validation of bean definition failed", ex);
  14.         }
  15.     }
  16.     // 为了保证数据一致性,注册时加个synchronized线程锁
  17.     synchronized (this.beanDefinitionMap) {
  18.         // 检查在IoC容器中是否有同名bean,有同名的还不让覆盖的就是抛异常
  19.         BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  20.         if (oldBeanDefinition != null) {
  21.             if (!this.allowBeanDefinitionOverriding) {
  22.                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  23.                                                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
  24.                                                        "': There is already [" + oldBeanDefinition + "] bound.");
  25.             }
  26.             else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
  27.                 if (this.logger.isWarnEnabled()) {
  28.                     this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +" with a framework-generated bean definition ': replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");
  29.                 }
  30.             }
  31.             else {
  32.                 if (this.logger.isInfoEnabled()) {
  33.                     this.logger.info("Overriding bean definition for bean '" + beanName +"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
  34.                 }
  35.             }
  36.         }
  37.         else {
  38.             this.beanDefinitionNames.add(beanName);
  39.             this.frozenBeanDefinitionNames = null;
  40.         }
  41.         // 把BeanDefinition装到如到beanDefinitionMap中
  42.         // 【至此Spring IoC容器初始化完成~】
  43.         // beanDeinitionMap是初始长度64的ConcurrentHashMap
  44.         this.beanDefinitionMap.put(beanName, beanDefinition);
  45.     }
  46.  
  47.     resetBeanDefinition(beanName);
  48. }

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

三、小结

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

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


本文参考:

  1. Spring揭秘,王福强

  2. Spring技术内幕,计文柯

  3. Spring源码深度分析,郝佳

  4. 深入理解-Spring-之源码剖析IOC

  5. 浅谈控制反转与依赖注入

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号