有没有想过 Tomcat 启动后,你的 Spring 中的各种组件各种 Bean 就准备好了,怎么准备好的?具体是经过了怎样的流程?

IOC 启动入口

在说明 Bean 实例化的过程前,先了解整个 Web 容器以及 Spring IOC 容器启动的大流程,先搞明白是如何一步一步走到实例化过程。

WebApplicationContext

通常 Spring web 项目都会部署在 Tomcat 这个 Web 容器下,那么 Tomcat 这个 Web 容器是如何与 Spring 这个 IOC 容器建立关系?
Spring 为 Web 项目准备了WebApplicationContext,它允许相对于 Web 根目录的路径中装载配置文件完成初始化工作。这可以想到我们在做 Spring Web 项目中配置 web.xml 文件的一些配置。

  1. <listener>
  2. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  3. </listener>
  4. <context-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:spring/application.xml</param-value>
  7. </context-param>

上面的配置文件是不是很熟悉,你建立一个 Spring Web 项目部署在 Tomcat 下,那么上面的配置直接复制过来改下配置文件路径即可。这些配置就是为了准备我们的 Spring 容器。(如果是SpringBoot项目,这一步框架都帮你做好了)

Spring Web 在 Web 容器看来始终是一个小项目(乙方),需要等待 Web 容器(甲方:例如 Tomcat)启动后,听甲方指示去启动自己。乙方启动的时候能得到甲方的 ServletContext(看成一份合同),然后乙方将自己的上下文信息注册到甲方的 ServletContext 中(签订合同),表示我是小弟,随时听从召唤。具体操作如下: WebApplicationContext 定义了一个常量 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext 实例即以此为 key 放置在 ServletContext 的属性列表里。

从源码中可以看见常量配置:
spring-web | org.springframework.web.context.WebApplicationContext

  1. public interface WebApplicationContext extends ApplicationContext {
  2. String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
  3. String SCOPE_REQUEST = "request";
  4. String SCOPE_SESSION = "session";
  5. String SCOPE_APPLICATION = "application";
  6. String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
  7. String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
  8. String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
  9. @Nullable
  10. ServletContext getServletContext();
  11. }

示意图:
Spring5.2 源码学习 - IOC 容器启动过程 - 图1

ContextLoaderListener

下一个重要的角色就是 ContextLoaderListener 监听器。ContextLoaderListener 监听器的作用就是启动 Web 容器时,自动装配 WebApplicationContext 。因为它实现了ServletContextListener 这个接口,在web.xml 配置这个监听器,启动容器时,就会默认执行它实现的方法。
spring-web | org.springframework.web.context.ContextLoaderListener

  1. public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
  2. public ContextLoaderListener() {
  3. }
  4. public ContextLoaderListener(WebApplicationContext context) {
  5. super(context);
  6. }
  7. @Override
  8. public void contextInitialized(ServletContextEvent event) {
  9. initWebApplicationContext(event.getServletContext());
  10. }
  11. @Override
  12. public void contextDestroyed(ServletContextEvent event) {
  13. closeWebApplicationContext(event.getServletContext());
  14. ContextCleanupListener.cleanupAttributes(event.getServletContext());
  15. }
  16. }

servlet-api.jar 提供了接口 ServletContextListener

  1. public interface ServletContextListener extends EventListener {
  2. // 初始化
  3. public void contextInitialized(ServletContextEvent sce);
  4. // 销毁
  5. public void contextDestroyed(ServletContextEvent sce);
  6. }

ContextLoaderListener 只是个入口,整个加载配置过程由其父类 ContextLoader 来启动。 自 initWebApplicationContext 方法开始,正式开启 Spring 容器的初始化旅程!

  1. /**
  2. * Initialize the root web application context.
  3. */
  4. @Override
  5. public void contextInitialized(ServletContextEvent event) {
  6. initWebApplicationContext(event.getServletContext());
  7. }

IOC 启动过程

知道了如何开启容器初始化,那么接下来看下整个实例化过程,先站在全局看都做了哪些工作,随后的文章根据重要的节点做源码的学习。不太可能每个地方都去看源码 。
那么还是从 ContextLoader 的 initWebApplicationContext 方法开始:下面的代码删除了一些打印日志等,不影响代码阅读。

整体:初始 ApplicationContext

spring-web | org.springframework.web.context.ContextLoader#initWebApplicationContext

  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  2. // 判断下,如果Web容器中已经注册了同名的,说明重复初始化了,抛异常
  3. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  4. throw new IllegalStateException(
  5. "Cannot initialize context because there is already a root application context present - " +
  6. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  7. }
  8. try {
  9. if (this.context == null) {
  10. // 创建我们的 WebApplicationContext
  11. this.context = createWebApplicationContext(servletContext);
  12. }
  13. // 这里会走,因为 createWebApplicationContext 创建的是 XmlWebApplicationContext
  14. if (this.context instanceof ConfigurableWebApplicationContext) {
  15. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  16. if (!cwac.isActive()) {
  17. // The context has not yet been refreshed -> provide services such as
  18. // setting the parent context, setting the application context id, etc
  19. if (cwac.getParent() == null) {
  20. // The context instance was injected without an explicit parent ->
  21. // determine parent for root web application context, if any.
  22. ApplicationContext parent = loadParentContext(servletContext);
  23. cwac.setParent(parent);
  24. }
  25. // 创建完上下文后,完善上下文的配置
  26. configureAndRefreshWebApplicationContext(cwac, servletContext);
  27. }
  28. }
  29. // 登记,和开头的判断对应
  30. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  31. ClassLoader ccl = Thread.currentThread().getContextClassLoader();
  32. if (ccl == ContextLoader.class.getClassLoader()) {
  33. currentContext = this.context;
  34. } else if (ccl != null) {
  35. currentContextPerThread.put(ccl, this.context);
  36. }
  37. return this.context;
  38. } catch (RuntimeException | Error ex) {
  39. logger.error("Context initialization failed", ex);
  40. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  41. throw ex;
  42. }
  43. }

阶段:创建 ApplicationContext

从如下的方法开始:
spring-web | org.springframework.web.context.ContextLoader#createWebApplicationContext

  1. protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
  2. // 确定应用上下文使用的Class
  3. Class<?> contextClass = determineContextClass(sc);
  4. // 上下文必须是ConfigurableWebApplicationContext的子类
  5. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  6. throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
  7. "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
  8. }
  9. // 实例化Class,转换成ConfigurableWebApplicationContext返回
  10. return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  11. }

方法:createWebApplicationContext这块代码整体不难理解,利用工具类创建对象。
所以得到 Class 的方法 determineContextClass 才是重点,如下所示:
spring-web | org.springframework.web.context.ContextLoader#determineContextClass

  1. protected Class<?> determineContextClass(ServletContext servletContext) {
  2. // 从 servletContext加载的web.xml中读取配置的 context类信息。我记得自己好像没有配置过。
  3. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
  4. if (contextClassName != null) {
  5. try {
  6. return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
  7. }
  8. catch (ClassNotFoundException ex) {
  9. throw new ApplicationContextException(
  10. "Failed to load custom context class [" + contextClassName + "]", ex);
  11. }
  12. }
  13. // 如果没有配置contextClassName,则从defaultStrategies缓存中拿到默认的WerApplicationContext对应的ClassName
  14. // 也就是从配置文件 ContextLoader.properties 读取的默认配置,也就是使用的 XmlWebApplicationContext
  15. // org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
  16. else {
  17. contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
  18. try {
  19. // 工具类实例化对象
  20. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
  21. }
  22. catch (ClassNotFoundException ex) {
  23. throw new ApplicationContextException(
  24. "Failed to load default context class [" + contextClassName + "]", ex);
  25. }
  26. }
  27. }
  • 首先根据 web.xml 中的 contextClass 参数来创建 context,没有的话走默认配置;
  • 默认配置从系统中的默认配置文件 ContextLoader.properties 读取,创建的是 XmlWebApplicationContext。

查一下 XmlWebApplicationContext 的继承关系:
image.png

阶段:配置 ApplicationContext

上一步拿到 XmlWebApplicationContext 后,需要做一些配置。

  1. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
  2. // 1.生成一个上下文的ID
  3. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  4. // The application context id is still set to its original default value
  5. // -> assign a more useful id based on available information
  6. String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
  7. if (idParam != null) {
  8. wac.setId(idParam);
  9. }
  10. else {
  11. // Generate default id...
  12. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  13. ObjectUtils.getDisplayString(sc.getContextPath()));
  14. }
  15. }
  16. // 2.给上下文设置ServletContext
  17. wac.setServletContext(sc);
  18. // 3.从ServletContext中解析配置文件的地址
  19. String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
  20. if (configLocationParam != null) {
  21. // 将配置文件的地址配置给上下文
  22. wac.setConfigLocation(configLocationParam);
  23. }
  24. // The wac environment's #initPropertySources will be called in any case when the context
  25. // is refreshed; do it eagerly here to ensure servlet property sources are in place for
  26. // use in any post-processing or initialization that occurs below prior to #refresh
  27. ConfigurableEnvironment env = wac.getEnvironment();
  28. if (env instanceof ConfigurableWebEnvironment) {
  29. // 4.初始化属性信息
  30. ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
  31. }
  32. // 5.用户自定义上下文配置
  33. customizeContext(sc, wac);
  34. // 6.刷新上下文配置
  35. wac.refresh();
  36. }

1. 自动ID

  1. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  2. // The application context id is still set to its original default value
  3. // -> assign a more useful id based on available information
  4. String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
  5. if (idParam != null) {
  6. wac.setId(idParam);
  7. }
  8. else {
  9. // Generate default id...
  10. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  11. ObjectUtils.getDisplayString(sc.getContextPath()));
  12. }
  13. }

2. 设置ServletContext

  1. wac.setServletContext(sc);

3. 解析配置文件地址

setConfigLocation 设置配置文件地址

  1. public void setConfigLocation(String location) {
  2. // 解析配置文件路径。",; \t\n" 分割成多个地址
  3. setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
  4. }
  5. public void setConfigLocations(@Nullable String... locations) {
  6. if (locations != null) {
  7. Assert.noNullElements(locations, "Config locations must not be null");
  8. this.configLocations = new String[locations.length];
  9. for (int i = 0; i < locations.length; i++) {
  10. // 遍历每个配置文件的路径,解析给定路径,将占位符替换为相应的环境属性值(如果需要)
  11. this.configLocations[i] = resolvePath(locations[i]).trim();
  12. }
  13. }
  14. else {
  15. this.configLocations = null;
  16. }
  17. }

resolvePath 处理配置文件路径
  1. protected String resolvePath(String path) {
  2. return getEnvironment().resolveRequiredPlaceholders(path);
  3. }
  4. public ConfigurableEnvironment getEnvironment() {
  5. if (this.environment == null) {
  6. this.environment = createEnvironment();
  7. }
  8. return this.environment;
  9. }

createEnvironment()
  1. // 可以被子类重载
  2. protected ConfigurableEnvironment createEnvironment() {
  3. return new StandardEnvironment();
  4. }
  5. // 最终走的是 AbstractRefreshableWebApplicationContext # createEnvironment
  6. protected ConfigurableEnvironment createEnvironment() {
  7. return new StandardServletEnvironment();
  8. }

看下 StandardServletEnvironment 的类继承关系:
image.png

实例化 StandardServletEnvironment 首先会走父类的构造:

  1. public AbstractEnvironment() {
  2. customizePropertySources(this.propertySources);
  3. }

回调方法,StandardServletEnvironment # customizePropertySources:

  1. @Override
  2. protected void customizePropertySources(MutablePropertySources propertySources) {
  3. // 添加servletConfigInitParams属性源,占位符会被替换
  4. propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
  5. // 添加servletContextInitParams属性源,占位符会被替换
  6. propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
  7. if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
  8. // 添加jndiProperties属性源
  9. propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
  10. }
  11. // 父类添加属性源,见下面的 【代码 Bt91cny9TevgRXJy8ycY】
  12. super.customizePropertySources(propertySources);
  13. }

【代码 Bt91cny9TevgRXJy8ycY】StandardEnvironment # customizePropertySources

  1. @Override
  2. protected void customizePropertySources(MutablePropertySources propertySources) {
  3. // 添加systemProperties属性源
  4. propertySources.addLast(
  5. new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
  6. // 添加systemEnvironment属性源
  7. propertySources.addLast(
  8. new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
  9. }

4. 初始化属性相关

根据环境参数初始化属性信息

  1. ConfigurableEnvironment env = wac.getEnvironment();
  2. if (env instanceof ConfigurableWebEnvironment) {
  3. // 4.初始化属性信息
  4. ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
  5. }

在3步中,为了解析配置文件路径创建了环境变量对象StandardServletEnvironment。然后调用initPropertySources方法将其中的占位符解析成真正的资源地址。

5. customizeContext 自定义配置

  1. customizeContext(sc, wac);

加载上下文初始化类。这个也是提供给用户的扩展点。
【代码 DiF8w8Etm85ywf5yZw2id】 ContextLoader # customizeContext

  1. protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
  2. // 1.加载上下文的需要初始化的类,见【代码 VPfrFavxxY3_sxJ0iMV7W】
  3. List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
  4. determineContextInitializerClasses(sc);
  5. // 2.遍历校验参数是否合法
  6. for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
  7. Class<?> initializerContextClass =
  8. GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
  9. if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
  10. throw new ApplicationContextException(String.format(
  11. "Could not apply context initializer [%s] since its generic parameter [%s] " +
  12. "is not assignable from the type of application context used by this " +
  13. "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
  14. wac.getClass().getName()));
  15. }
  16. // 3.实例化initializerClass, 并添加到contextInitializers中
  17. this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
  18. }
  19. // 4.对实例化的initializerClass排个序
  20. AnnotationAwareOrderComparator.sort(this.contextInitializers);
  21. // 5.initializer.initialize 进行自定义操作
  22. for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
  23. initializer.initialize(wac);
  24. }
  25. }

determineContextInitializerClasses

上下文需要初始化的类 【代码 VPfrFavxxY3_sxJ0iMV7W】

  1. protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
  2. determineContextInitializerClasses(ServletContext servletContext) {
  3. List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
  4. new ArrayList<>();
  5. // 从servletContext解析配置在web.xml的参数globalInitializerClasses
  6. String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
  7. if (globalClassNames != null) {
  8. // 如果配置不为空,多个类分割,实例化类实例后加入集合中,注意不是实例化对象
  9. for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
  10. classes.add(loadInitializerClass(className));
  11. }
  12. }
  13. // 从servletContext解析配置在web.xml的参数contextInitializerClasses
  14. String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
  15. if (localClassNames != null) {
  16. // 如果配置不为空,多个类分割,实例化类实例后加入集合中,注意不是实例化对象,见 【代码 F9A_stT7WYPCnoTwdSFQ】
  17. for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
  18. classes.add(loadInitializerClass(className));
  19. }
  20. }
  21. return classes;
  22. }

【代码 F9A_stT7WYPCnoTwdSFQ】 loadInitializerClass ,反射加载类。

  1. // 反射类加载
  2. @SuppressWarnings("unchecked")
  3. private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
  4. try {
  5. Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
  6. if (!ApplicationContextInitializer.class.isAssignableFrom(clazz)) {
  7. throw new ApplicationContextException(
  8. "Initializer class does not implement ApplicationContextInitializer interface: " + clazz);
  9. }
  10. return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
  11. }
  12. catch (ClassNotFoundException ex) {
  13. throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
  14. }
  15. }

用户自定义扩展代码演示
  1. public class SpringApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  2. @Override
  3. public void initialize(ConfigurableApplicationContext applicationContext) {
  4. System.out.println("customizeContext,用户自定义扩展点!");
  5. // 添加BeanFactoryPostProcessor
  6. MyBeanFactoryPostProcessor myBeanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
  7. applicationContext.addBeanFactoryPostProcessor(myBeanFactoryPostProcessor);
  8. }
  9. }

web.xml 增加配置;

  1. <context-param>
  2. <param-name>contextInitializerClasses</param-name>
  3. <param-value>
  4. cn.lichenghao.context.SpringApplicationContextInitializer
  5. </param-value>
  6. </context-param>

6. 刷新上下文,准备IOC启动

  1. wac.refresh();

阶段:IOC 容器启动

上一步完成配置后,最终会调用 wac.refresh();

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. // Prepare this context for refreshing.
  4. prepareRefresh();
  5. // Tell the subclass to refresh the internal bean factory.
  6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  7. // Prepare the bean factory for use in this context.
  8. prepareBeanFactory(beanFactory);
  9. try {
  10. // Allows post-processing of the bean factory in context subclasses.
  11. postProcessBeanFactory(beanFactory);
  12. // Invoke factory processors registered as beans in the context.
  13. invokeBeanFactoryPostProcessors(beanFactory);
  14. // Register bean processors that intercept bean creation.
  15. registerBeanPostProcessors(beanFactory);
  16. // Initialize message source for this context.
  17. initMessageSource();
  18. // Initialize event multicaster for this context.
  19. initApplicationEventMulticaster();
  20. // Initialize other special beans in specific context subclasses.
  21. onRefresh();
  22. // Check for listener beans and register them.
  23. registerListeners();
  24. // 完成不是懒加载的Bean的实例化工作
  25. // Instantiate all remaining (non-lazy-init) singletons.
  26. finishBeanFactoryInitialization(beanFactory);
  27. // 完成刷新,通知生命周期处理器lifecycleProcessor,发出ContextRefreshEvent,通知订阅人
  28. // Last step: publish corresponding event.
  29. finishRefresh();
  30. }
  31. catch (BeansException ex) {
  32. if (logger.isWarnEnabled()) {
  33. logger.warn("Exception encountered during context initialization - " +
  34. "cancelling refresh attempt: " + ex);
  35. }
  36. // Destroy already created singletons to avoid dangling resources.
  37. destroyBeans();
  38. // Reset 'active' flag.
  39. cancelRefresh(ex);
  40. // Propagate exception to caller.
  41. throw ex;
  42. }
  43. finally {
  44. // Reset common introspection caches in Spring's core, since we
  45. // might not ever need metadata for singleton beans anymore...
  46. resetCommonCaches();
  47. }
  48. }
  49. }

这是一个典型的模板方法设计模式,这里完成了整个 IOC 容器的启动过程。

方法 功能
prepareRefresh 准备工作,如:创建environment 并加载 System.properties() 及 System.getenv() 到 environment 中;
obtainFreshBeanFactory 创建 BeanFactory 初始化所有的 BeanDefine。XmlBeanDefinitionReader 来读取所有Bean的定义,存储在DefaultListableBeanFactory 的 beanDefinitionMap 里面;
prepareBeanFactory 在 Bean 生命周期中有很多的扩展点,这些扩展点的工厂类在这里处理;
postProcessBeanFactory 扩展点,子类可以重写该方法完成一些需要在 BeanFactory 创建后执行的操作;
invokeBeanFactoryPostProcessors 执行上一步的扩展;
registerBeanPostProcessors 注册上所有的 BeanPostProcessors,所有 BeanPostProcessor 类型的 Bean 会在此时完成实例化;
initMessageSource 国际化
initApplicationEventMulticaster 初始化事件广播器,当事件发布器发布事件时,会调用;
onRefresh 扩展点,子类可以重写该方法,完成一些定制的功能;
registerListeners 注册监听器;
finishBeanFactoryInitialization 完成不是懒加载的Bean的实例化工作;
finishRefresh 最后一步:发布相应的事件

附录

注入Bean的方式

  • 构造方法 ```xml

  1. - 静态工厂
  2. ```xml
  3. <bean id="userService2" class="cn.com.lichenghao.factory.StaticUserFactory" factory-method="instance"></bean>