-- 以下内容均基于2.1.8.RELEASE版本
紧接着上一篇(三)SpringBoot启动过程的分析-创建应用程序上下文,本文将分析上下文创建完毕之后的下一步操作:预处理上下文容器。
预处理上下文容器预处理上下文容器由prepareContext()方法完成,本篇内容全部都是基于这个方法所涉及的内容进行分析。
// SpringApplication.java private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置环境对象,传入的对象是解析完毕profiles的对象,Context内部则是不完整的对象 context.setEnvironment(environment); // 设置上下文参数 postProcessApplicationContext(context); // 加载ApplicationContextInitializers applyInitializers(context); // 触发开始准备上下文事件 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 将启动参数包装为名为springApplicationArguments的DefaultApplicationArguments对象,并以单例模式注册 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); // 设置打印的Banner if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 设置是否允许覆盖BeanDefinition if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 加载资源 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); // 触发上下文加载完毕事件 listeners.contextLoaded(context); } 设置上下文参数 // SpringApplication.java protected void postProcessApplicationContext(ConfigurableApplicationContext context) { // 以单例模式注册beanNameGenerator if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } // 为上下文设置资源加载器和类加载器 if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader()); } } // 为上下文设置转换服务 if (this.addConversionService) { context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); } }将前面步骤初始化的属性赋值给上下文容器,代码中的this代表的是SpringApplication。
加载ApplicationContextInitializers在SpringApplication.run()的一开始它就通过SPI获取到所有的ApplicationContextInitializers,在这里他们将被执行。
protected void applyInitializers(ConfigurableApplicationContext context) { // 调用每一个实现类的initialize方法 for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } } // 将之前获取到的集合进行排序并返回只读集合 public Set<ApplicationContextInitializer<?>> getInitializers() { return asUnmodifiableOrderedSet(this.initializers); } DelegatingApplicationContextInitializer用于初始化配置文件(属性名:context.initializer.classes)中指定的ApplicationContextInitializer实现类
public void initialize(ConfigurableApplicationContext context) { // 获取环境对象 ConfigurableEnvironment environment = context.getEnvironment(); // 获取context.initializer.classes属性指定的实现类 List<Class<?>> initializerClasses = getInitializerClasses(environment); if (!initializerClasses.isEmpty()) { // 调用其initialize()方法 applyInitializerClasses(context, initializerClasses); } } private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) { // 通过指定属性去获取,此处常量属性值为context.initializer.classes String classNames = env.getProperty(PROPERTY_NAME); List<Class<?>> classes = new ArrayList<>(); if (StringUtils.hasLength(classNames)) { // 根据代码可以推断出,context.initializer.classes的值可以用逗号拼接 for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) { classes.add(getInitializerClass(className)); } } return classes; }这里看起来好像很奇怪,本身当前的类就是一个ApplicationContextInitializer, 它已经被上游代码调用了initializer()方法,在initializer()方法中它又去获取ApplicationContextInitializer,然后接着调用initializer(),好像很绕。不过它的类描述已经说明了问题,它用于加载从配置文件指定
的那些ApplicationContextInitializer。如果阅读过第一篇概览SpringApplication.java #构造方法的话就会明白,当前对象就是在SpringApplication类的构造方法中通过SPI方式获取到的,而当前方法则是通过配置文件指定的方式
来获取。由此就可以得出一个结论:在SpringBoot中实现ApplicationContextInitializer并确保其被加载有三种方法,一是通过SpringApplication公开的addInitializers()方法直接添加,二是以SPI方式配置,三是以配置文件方式配置。
在SpringBoot中三种配置ApplicationContextInitializer的方法:
直接以代码方式添加
new SpringApplication(Example.class).addInitializers(new 实现类());
以SPI方式
在/META-INF/spring.factories文件中配置org.springframework.context.ApplicationContextInitializer=实现类A, 实现类B