阅读 59

Spring IOC-容器启动

这是我参与8月更文挑战的第29天,活动详情查看: 8月更文挑战

本文使用的是4.3.20版本的spring,是5.x之前的比较新的版本。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.20.RELEASE</version>
</dependency>
复制代码
// 定义一个接口
public interface MessageService {

    void sayHello(String name);
}
// 接口实现了类
public class MessageServiceImpl implements MessageService {

    @Override
    public void sayHello(String name) {
        System.out.println(name + "say hello");
    }
}
// 容器启动类
public class Main {
    public static void main(String[] args) {
        final ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                "application.xml");
        final MessageService messageService = (MessageService) applicationContext
                .getBean("messageService");
        // 执行
        messageService.sayHello("小明同学");
    }
}
复制代码
<!--application.xml 配置文件-->
<bean id="messageService" class="*.MessageServiceImpl" />
复制代码

以上例子很简单,不过也够引出本文的主题了,就是怎么样通过配置文件来启动 Spring 的 ApplicationContext ?也就是我们今天要分析的 IOC 的核心了。ApplicationContext 启动过程中,会负责创建实例 Bean,往各个 Bean 中注入依赖等。

除了ClassPathXmlApplicationContext ,还有两个类FileSystemXmlApplicationContextAnnotationConfigApplicationContextFileSystemXmlApplicationContext 的构造函数需要一个 xml 配置文件在系统中的路径,其他和 ClassPathXmlApplicationContext 基本上一样,AnnotationConfigApplicationContext 是基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置,是比较简单的方式,也是大势所趋吧。

BeanFactory,从名字上也很好理解,生产 bean 的工厂,它负责生产和管理各个 bean 实例。IoC 中最核心的接口是 BeanFactory ,提供 IoC 的高级服务,而 ApplicationContext 是建立在 BeanFactory 基础之上提供抽象的面向应用的服务。 ApplicationContext

从上图可以看出:

  1. ApplicationContext 继承了 ListableBeanFactory,这个Listable的意思就是,通过这个接口,我们可以获取多个 Bean,大家看源码会发现,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。
  2. ApplicationContext 继承了 HierarchicalBeanFactoryHierarchical 单词本身已经能说明问题了,也就是说我们可以在应用中起多个 BeanFactory,然后可以将各个BeanFactory设置为父子关系。

除了上面两个继承之外,ApplicationContext还提供了AutowireCapableBeanFactory getAutowireCapableBeanFactory()方法。

BeanFactory和ApplicationContext

从ClassPathXmlApplicationContext开始

ClassPathXmlApplicationContext

整个链路复杂到可怕,所以想要真正读懂这部分代码需要耐心的一步步debug下去。

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

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

接下来,就是 refresh(),这里简单说下为什么是 refresh(),而不是 init() 这种名字的方法。因为 ApplicationContext 建立起来以后,其实我们是可以通过调用 refresh() 这个方法重建的,refresh() 会将原来的 ApplicationContext 销毁,然后再重新执行一次初始化操作。

public void refresh() throws BeansException, IllegalStateException {
   // 加锁,防止refresh()还未结束,又有新的容器启动或者销毁动作
   synchronized (this.startupShutdownMonitor) {
      // 准备工作,记录容器的启动时间,标记容器的启动状态为已启动,处理文件中的占位符
      prepareRefresh();

      /*
      	这一步比较重要,这步完成后,配置文件就会解析成一个个Bean 定义,注册到 BeanFactory中,
      	当然这里说的Bean还未完成初始化,只是将配置信息提取出来,并将这些信息保存起来。
      */
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 给beanFactory设置一些属性:环境、系统属性
      prepareBeanFactory(beanFactory);

      try {
         // 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
         // 那么在容器初始化以后,Spring会负责调用里面的 postProcessBeanFactory 方法。】

         // 这里是提供给子类的扩展点,到这里的时候,所有的Bean都加载、注册完成了,
         // 但是都还没有初始化
         postProcessBeanFactory(beanFactory);

         // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
         // 此接口两个方法: postProcessBeforeInitialization和postProcessAfterInitialization
         // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化MessageSource组件,负责国际化处理,消息的绑定和解析
         initMessageSource();

         // 初始化当前 ApplicationContext 的事件广播器
         initApplicationEventMulticaster();

         // 空实现,钩子方法,用于子类实现特殊bean的初始化
         onRefresh();

         // 注册事件监听器,监听器需要实现 ApplicationListener 接口
         registerListeners();

         // 初始化所有的 singleton beans
         finishBeanFactoryInitialization(beanFactory);

         // 最后,广播事件,ApplicationContext 初始化完成
         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容器的启动过程,下面会对refresh()方法中重要的部分进行拆解。整个流程还是十分清晰的,但是每一步的代码都埋的十分深,读起来可能有点费劲。

文章分类
后端
文章标签