2.5.自动配置
在自动装载步骤中已经获得需要加载的自动配置类的全路径,接下来就是自动配置。
以随便一个AutoConfiguration类为例:
头上的一大串@Conditional注解其实就是过滤时的过滤条件,过滤列表其实就是通过这些条件注解生成的。
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(HttpProperties.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration {}
这个@Configuration满足条件后其中的@Bean都会被自动装入IOC。
3.启动过程
3.1.整体流程
Spring boot的启动过程就是围绕上下文的创建、准备、刷新(填充)展开的
spring应用上下文和servletContext不是一个东西,servlet上下文用来维系当前应用的一块共享空间,目的是实现资源和数据在应用中的全局共享。spring的上下文是一个维护Bean定义以及对象之间协作关系的高级接口,目的是维护好整个spring中的资源,如配置文件、Bean对象等,其涵盖了IOC,但不只有IOC,可以理解为Spring应用的一个抽象。
在SpringApplication的run()方法中创建应用上下文,整个SpringApplication的run方法主要完成四个核心动作:
prepareEnvironment
创建环境信息对象,解析环境参数,包含配置文件、命令行传参等。
createApplicationContext
创建应用上下文对象
prepareContext
准备应用上下文对象
refreshContext
- 刷新应用上下文对象
// 类 SpringApplication 代码片段 public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { // 包装通过命令行传入的名命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 结合命令行参数 准备环境对象,该环境对象将会被设置到应用上下文对象 ApplicationContext 上 , // 环境对象通常包含如下信息 : // 1. profile // 2. system properties // 3. system environment // 4. commandline arguments // 5. spring 配置文件 // 6. 一个随机值属性源 random // 对于当前 WebFlux 应用来讲,这里实现类会使用 StandardReactiveWebEnvironment ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); // 创建应用上下文对象 ApplicationContext // 实现类会采用 : AnnotationConfigReactiveWebServerApplicationContext context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 准备应用上下文对象 ApplicationContext // 1. 关联环境信息对象到应用上下文对象 // 2. 对象创建后置处理 : 设置容器的类型转换服务 // 3. 初始化应用上下文对象:调用各个 ApplicationContextInitializer // 4. 广播事件 : ApplicationContextInitializedEvent // 5. 将应用程序参数作为一个 bean 注册到容器 : springApplicationArguments // 6. 将应用程序入口类作为 bean 注册到容器 (load) // 7. 上下文加载完成生命周期事件回调,为各个实现了 接口 ApplicationContextAware 的 // ApplicationListener 设置应用上下文对象属性, 并广播事件 : ApplicationPreparedEvent prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新应用上下文对象 ApplicationContext // 主要是调用应用上下文对象 ApplicationContext 自身的 refresh 方法 refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); // 应用程序上下文对象 ApplicationContext 已经准备就绪, // 现在调用各种开发人员或者框架其他部分定义的 // ApplicationRunner 或者 CommandLineRunner callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
3.2.创建环境信息对象
// SpringApplication 代码片段 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment // 创建环境信息对象 environment ConfigurableEnvironment environment = getOrCreateEnvironment(); // 将应用程序参数关联到环境信息对象 environment configureEnvironment(environment, applicationArguments.getSourceArgs()); // 发布应用程序事件 : 环境信息对象准备好了 , // 同步调用各个事件监听器 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
普通web应用和reactive创建的环境信息对象类型不同,但是实际功能相同,并没有什么太大区别。
// SpringApplication 代码片段 private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); } }
3.3.创建应用上下文对象
根据之前环境推断中得到的当前应用的环境类型来创建不同类型的应用上下文。
// SpringApplication 代码片段 protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { // 根据 this.webApplicationType 确定应用上下文实现类 switch (this.webApplicationType) { case SERVLET: // DEFAULT_SERVLET_WEB_CONTEXT_CLASS 常量值为 : // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext // 对应 Spring MVC Servlet Web 环境 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: // DEFAULT_REACTIVE_WEB_CONTEXT_CLASS 常量值为 : // org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext // 对应 Spring WebFlux Reactive Web 环境 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: // DEFAULT_CONTEXT_CLASS 常量值为 : // org.springframework.context.annotation.AnnotationConfigApplicationContext // 不对应任何 Web 环境 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } // 确定应用上下文实现类之后,实例化应用上下文对象 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
3.4.刷新应用上下文对象
3.4.1.准备刷新
这一步主要是完成刷新前的准备工作,将除IOC相关的一切context中的东西全部赋值初始化好。
主要完成以下动作:
- 关联环境信息
- 查找调用各种前置、后置处理器(自定义的、自带的)
- 调用各种回调
- 获取主启动类的路径,将主启动类封装成一个BeanDefinition
// SpringApplication 代码片段 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 1. 关联环境信息对象到应用上下文对象 context.setEnvironment(environment); // 2. 对象创建后置处理 : 设置容器的类型转换服务 postProcessApplicationContext(context); // 3. 初始化应用上下文对象:调用各个 ApplicationContextInitializer applyInitializers(context); // 4. 广播事件 : ApplicationContextInitializedEvent listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans // 5. 将应用程序参数作为一个 bean 注册到容器 : springApplicationArguments ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 获取主启动类的路径 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 6. 将主启动类封装成一个BeanDefinition load(context, sources.toArray(new Object[0])); // 7. 上下文加载完成生命周期事件回调,为各个实现了 接口 ApplicationContextAware 的 // ApplicationListener 设置应用上下文对象属性, 并广播事件 : ApplicationPreparedEvent listeners.contextLoaded(context); }
3.4.2.刷新
主要是调用应用上下文对象 ApplicationContext 自身的 refresh 方法,这是上下文对象的初始化中最关键的一步,该步骤中会完成几个核心动作:
- 初始化IOC容器(即BeanFactory)
该步骤中就会扫描解析注解,触发自动装配.
初始化WebServer容器
刷新应用上下文的动作其实是在spring相关的jar中,因此首先要有个概念,在这一步之前spring boot的动作已经完成,真正与IOC相关的动作还是由spring来完成,所以说spring boot是对spring的二次封装。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 做一些初始化动作 prepareRefresh(); // 获取bean factory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 初始化bean factory,为其成员属性赋一些值 prepareBeanFactory(beanFactory); try { // 获取所有bean后置处理器 postProcessBeanFactory(beanFactory); // **最核心的方法,注解的扫描,自动配置类的装载,IOC的初始化等全在这个方法中 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. 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:
创建容器其实没有什么说的,就是new一个web server(tomcat、netty或者jetty)出来。这里着重要说一下初始化IOC。
入口在invokeBeanFactoryPostProcessors(beanFactory)。
IOC容器的初始化分为三步:
Resource定位
定位到需要的各种路径:
BasePackage
这一步在准备刷新的时候就已经完成,并在封装在了主启动类封装为的BeanDefinition中。
基于BasePackage去扫描通过注解自定义的需要注入IOC的Bean。
自动配置类的全路径
这一步在刷新应用上下文的时候进行,即去获取factory.properties。
基于自动配置类的全路径去将对应自动配置类注入IOC。
BeanDefinition载入
将定位到的Resource记录的Class分别封装为一个个的Definition。
BeanDefinition注册
将Definition注册进IOC中。其实就是注入到一个ConcurrentHashMap中,IOC就是通过这个Map来持有这些BeanDefinition的。
IOC涉及的两个核心概念:
BeanDefinition
BeanFactory
IOC容器其实就是BeanFactory,BeanFactory就是IOC容器的规范接口,有多个实现,最典型的就是DefalutListableBeanFactory,IOC容器中有一个成员Map(BeanDefinitionMap),该Map持有所有的BeanDefinition,用来维护Bean的基本信息(class、作用域等)