spring ClassPathXmlApplicationContext学习

前言

这篇文章主要借鉴了一位大佬的文章,提炼了一下里面的内容,如果想更深入的学习可以:
点击这里

开始

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.11.RELEASE</version>
</dependency>

先用maven引入该依赖。
在这里插入图片描述
由上图可知我们要的只是核心容器的功能,那么只要引入spring的context就行了。context是什么?其实只要把它看成是一个全局的以beanName为key,beanDefinition为value的HashMap就行了,通过beanName来取beanDefinition。

先上一段简单代码:

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
}

这其实就是把ApplicationContext读取classpath(src目录下以classpath指定名称)的xml配置文件,完成这一步之后就可以将bean实例化。具体可以再看一段代码:

public class App {
    public static void main(String[] args) {
        // 用我们的配置文件来启动一个 ApplicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
 
        System.out.println("context 启动成功");
 
        // 从 context 中取出我们的 Bean,而不是用 new MessageServiceImpl() 这种方式
        MessageService messageService = context.getBean(MessageService.class);
        // 这句将输出: hello world
        System.out.println(messageService.getMessage());
    }
}

此messageService是在xml中配置过的,所以可以实现getBean的功能。

BeanFactory

上文中所提及的ApplicationContext其实就是BeanFactory的一个派生接口
在这里插入图片描述
讲解一下ApplicationContext继承的两个类以及两个重要的类:

  1. ListableBeanFactory:Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,而BeanFactory这个顶层接口只能够获得单个Bean。
  2. HierarchicalBeanFactory:可以在应用中启动多个BeanFactory,可以将他们设置为父子关系。
  3. AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,但是仔细看上图,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。
  4. ConfigurableListableBeanFactory 也是一个特殊的接口,看图,特殊之处在于它继承了第二层所有的三个接口,而 ApplicationContext 没有。这点之后会用到。

以上四个类是最重要的4个类,接下来将要用到。

要研究ClassPathXmlApplicationContext就要先研究其构造方法:
其实仔细看了就发现除了无参构造和

//如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法
public ClassPathXmlApplicationContext(ApplicationContext parent) {
		super(parent);
	}

,其他构造都是调用了:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {
		super(parent);
		//根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
		setConfigLocations(configLocations);
		if (refresh) {
			//refresh();是核心方法
			refresh();
		}
	}

以及

public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
			throws BeansException {

		super(parent);
		Assert.notNull(paths, "Path array must not be null");
		Assert.notNull(clazz, "Class argument must not be null");
		this.configResources = new Resource[paths.length];
		for (int i = 0; i < paths.length; i++) {
			this.configResources[i] = new ClassPathResource(paths[i], clazz);
		}
		refresh();
	}

我们主要来看第一个。
refresh()是可以销毁原application并重建(重新初始化)的f方法,下面是refresh()的具体实现;

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
   synchronized (this.startupShutdownMonitor) {
      prepareRefresh();
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      prepareBeanFactory(beanFactory);
 
      try {
         postProcessBeanFactory(beanFactory);
         invokeBeanFactoryPostProcessors(beanFactory);
         registerBeanPostProcessors(beanFactory);
         initMessageSource();
         initApplicationEventMulticaster();
         onRefresh();
         registerListeners();
         finishBeanFactoryInitialization(beanFactory);
         finishRefresh();
      }
 
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         //销毁已经实例化的beans,防止占着资源
         destroyBeans();
         // Reset 'active' flag
         cancelRefresh(ex);
 
         // 把异常往外抛
         throw ex;
      }
 
      finally {
         resetCommonCaches();
      }
   }
}
  • synchronized (this.startupShutdownMonitor) :通过同步锁来防止一个ApplicationContext还没有被初始化成功就被销毁的情况。
  • prepareRefresh():准备refresh,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
  • ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory():这步显而易见的获得了一个beanFactory,讲未初始化的bean注册到了beanFactory
  • prepareBeanFactory(beanFactory):设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean,用来加载类。
  • rocessBeanFactory(beanFactory):这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
  • invokeBeanFactoryPostProcessors(beanFactory):调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
  • registerBeanPostProcessors(beanFactory):注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别,此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization,两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
  • initMessageSource():初始化当前 ApplicationContext 的 MessageSource
  • initApplicationEventMulticaster():初始化当前 ApplicationContext 的事件广播器
  • onRefresh():具体的子类可以在这里初始化一些特殊的 Bean
  • registerListeners():注册事件监听器,监听器需要实现 ApplicationListener 接口
  • finishBeanFactoryInitialization(beanFactory):初始化除了懒加载的类之外的所有的 singleton beans

总结

ApplicationContext虽然继承自BeanFactory,但是它真正用到的factory却是内部生成的DefaultListableBeanFactory的实例,而DefaultListableBeanFactory在上面的UML图中可以看到是最最底层,且实现最多的一个类,所以选择他肯定是功能最强大的。说到底ApplicationContext与BeanFactory相关的操作都是靠内部的beanFactory来实现的。

BeanDefinition

该接口的中文翻译是Bean定义,我们知道所有实例化的bean都会保存在beanFactory中,但是保存过程是怎么样的呢?其实就是把bean转化成BeanDefinition保存在beanFactory中,而beanDefinition则保存了bean的指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等信息。然而这里虽然保存了这些信息,却没有保存我们所需要的类的实例,实例的具体去向在下文交代。

customizeBeanFactory 和 loadBeanDefinitions

customizeBeanFactory

customizeBeanFactory(beanFactory) 就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用。
是否允许覆盖:就是当不同配置文件的bean有着相同id的时候,是报错还是让后者覆盖前者,默认是允许的

循环引用也很好理解:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。
默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。

loadBeanDefinitions
/** 我们可以看到,此方法将通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean。*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader
   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));
 
   // 初始化 BeanDefinitionReader,其实这个是提供给子类覆写的,
   // 我看了一下,没有类覆写这个方法,我们姑且当做不重要吧
   initBeanDefinitionReader(beanDefinitionReader);
   // 重点来了,继续往下
   loadBeanDefinitions(beanDefinitionReader);
}

这个代码其实就是一个字符输入流将xml中对于BeanDefinition的信息读取,然后通过loadBeanDefinitions就是加载的过程了,通过配置文件获取了所有需要的信息,完成了Bean的加载,成为了BeanDefinition。beanDefinitionMap就是存储我们所需要BeanDefinition的地方,用beanName映射BeanDefinition。

以上就讲完了obtainFreshBeanFactory的作用。

prepareBeanFactory

prepareBeanFactory(factory)主要用于生成类加载器,添加几个 BeanPostProcessor顺便手动注册几个bean。

方法名叫做准备bean工厂,而他生成了类加载器就是因为类需要去加载,而几个手动注册的bean是有特殊用处的。

初始化所有singleton beans

触发所有非懒加载的singleton beans。这里需要注意的是在初始化的时候是会合并父bean的配置的,父bean就属于属性的继承,有点类似Java的继承,很好理解,如果不懂可以看原文的附录,有详细的介绍。其实要注意配置了abstract = true的singletons是不用加载的,就像抽象类是无法实现的一样,他们只是为子类提供一些统一配置的。如果是FactoryBean的话还要在它的命名之前加上一个"&"符号作为标识。

对于普通的bean,当调用getBean(beanName);的时候就完成了初始化

getBean(beanName)
@Override
public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}
 
// 我们在剖析初始化 Bean 的过程,但是 getBean 方法我们经常是用来从容器中获取 Bean 用的,注意切换思路,
// 已经初始化过了就从容器中直接返回,否则就先初始化再返回
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
   // 获取一个 “正统的” beanName,处理两种情况,一个是前面说的 FactoryBean(前面带 ‘&’),
   // 一个是别名问题,因为这个方法是 getBean,获取 Bean 用的,你要是传一个别名进来,是完全可以的
   final String beanName = transformedBeanName(name);
 
   // 注意跟着这个,这个是返回值
   Object bean; 
 
   // 检查下是不是已经创建过了
   Object sharedInstance = getSingleton(beanName);
 
   // 这里说下 args 呗,虽然看上去一点不重要。前面我们一路进来的时候都是 getBean(beanName),
   // 所以 args 其实是 null 的,但是如果 args 不为空的时候,那么意味着调用方不是希望获取 Bean,而是创建 Bean
   if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("...");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      // 下面这个方法:如果是普通 Bean 的话,直接返回 sharedInstance,
      // 如果是 FactoryBean 的话,返回它创建的那个实例对象
      // (FactoryBean 知识,读者若不清楚请移步附录)
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
 
   else {
      if (isPrototypeCurrentlyInCreation(beanName)) {
         // 当前线程已经创建过了此 beanName 的 prototype 类型的 bean,那么抛异常
         throw new BeanCurrentlyInCreationException(beanName);
      }
 
      // 检查一下这个 BeanDefinition 在容器中是否存在
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // 如果当前容器不存在这个 BeanDefinition,试试父容器中有没有
         String nameToLookup = originalBeanName(name);
         if (args != null) {
            // 返回父容器的查询结果
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
      }
 
      if (!typeCheckOnly) {
         // typeCheckOnly 为 false,将当前 beanName 放入一个 alreadyCreated 的 Set 集合中。
         markBeanAsCreated(beanName);
      }
 
      /*
       * 稍稍总结一下:
       * 到这里的话,要准备创建 Bean 了,对于 singleton 的 Bean 来说,容器中还没创建过此 Bean;
       * 对于 prototype 的 Bean 来说,本来就是要创建一个新的 Bean。
       */
      try {
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);
 
         // 先初始化依赖的所有 Bean,这个很好理解。
         // 注意,这里的依赖指的是 depends-on 中定义的依赖
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               // 检查是不是有循环依赖,这里的循环依赖和我们前面说的循环依赖又不一样,这里肯定是不允许出现的,不然要乱套了,读者想一下就知道了
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               // 注册一下依赖关系
               registerDependentBean(dep, beanName);
               // 先初始化被依赖项
               getBean(dep);
            }
         }
 
         // 创建 singleton 的实例
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
               @Override
               public Object getObject() throws BeansException {
                  try {
                     // 执行创建 Bean,详情后面再说
                     return createBean(beanName, mbd, args);
                  }
                  catch (BeansException ex) {
                     destroySingleton(beanName);
                     throw ex;
                  }
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
 
         // 创建 prototype 的实例
         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               // 执行创建 Bean
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }
 
         // 如果不是 singleton 和 prototype 的话,需要委托给相应的实现类来处理
         else {
            String scopeName = mbd.getScope();
            final Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
            }
            try {
               Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                  @Override
                  public Object getObject() throws BeansException {
                     beforePrototypeCreation(beanName);
                     try {
                        // 执行创建 Bean
                        return createBean(beanName, mbd, args);
                     }
                     finally {
                        afterPrototypeCreation(beanName);
                     }
                  }
               });
               bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
               throw new BeanCreationException(beanName,
                     "Scope '" + scopeName + "' is not active for the current thread; consider " +
                     "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                     ex);
            }
         }
      }
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
   }
 
   // 最后,检查一下类型对不对,不对的话就抛异常,对的话就返回了
   if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
      try {
         return getTypeConverter().convertIfNecessary(bean, requiredType);
      }
      catch (TypeMismatchException ex) {
         if (logger.isDebugEnabled()) {
            logger.debug("Failed to convert bean '" + name + "' to required type '" +
                  ClassUtils.getQualifiedName(requiredType) + "'", ex);
         }
         throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
   }
   return (T) bean;
}

我再简化一下上述过程:首先先校验一下beanName,无论是BeanFactory还是别名都可以最终转化为“正统”的beanName。之后就是检查是否已经实例化过了该singletons,如果已经创建过了,会根据不同的情况有不同的做法,如果未创建过或者是prototype的话就是创建一个新的bean,注意在初始化该实例之前要先初始化所有它的依赖项,其实也很好理解,要生出你这个人的之前肯定要先生出手脚之类的,否则没有这些器官的“你”还是你么?但是这时的依赖关系仅存在于从配置读取的String[] dependsOn之中,当初始化这些实例之后还要注册到管理依赖关系的Map之中,之后就可以初始化被依赖项了,相当于该实例已经创建,现在注入到该实例中,其实仔细看代码这就是一种递归的关系。初始化实例的时候还要根据其具体的情况来进行实例化的过程,系统只定义了singleton和prototype两种情况,其他情况要靠自己扩展了。当我们创建完实例之后再去校验一下实例的类型,不对的话就抛异常,对的话就可以返回该实例了。实例化过程还设计参数设置等复杂过程,但是很多代码一时半会也无法完全理解,待日后再来细品。

总结

其实spring的Ioc就是把要管理的bean放入一个Map容器,以它的唯一标识beanName映射到bean的所有配置beanDefinition,之后就是根据Map的中的配置在spring容器启动的时候加载所有非懒加载的实例,这就是Ioc原理的大概内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值