01 Spring Boot 入口——main方法
@SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以分析 Spring Boot 启动过程,我们就从这两位开始。
02 核心注解
2.1 @SpringBootApplication
@SpringBootApplication 是最常用也几乎是必用的注解,源码如下:
从源码声明可以看出,@SpringBootApplication相当于 @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan ,因此我们直接拆开来分析。
2.2 @SpringBootConfiguration
@SpringBootConfiguration 是继承自Spring的 @Configuration 注解,@SpringBootConfiguration 作用相当于 @Configuration。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
spring 3.0中增加了@Configuration,@Bean。可基于JavaConfig形式对 Spring 容器中的bean进行更直观的配置。SpringBoot推荐使用基于JavaConfig的配置形式。
基于xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<bean id="mockService" class="..MockServiceImpl">
...
</bean>
</beans>
基于JavaConfig配置:
@Configuration
public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl();
}
}
总结,@Configuration相当于一个spring的xml文件,配合@Bean注解,可以在里面配置需要Spring容器管理的bean。
2.3 @ComponentScan
@ComponentScan源码:
基于xml配置:
<context:component-scan base-package="com.youzan" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
基于JavaConfig配置:
@Configuration
@ComponentScan(value = "com.youzan", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
})
public class ScanConfig {
}
总结:@ComponentScan通常与@Configuration一起配合使用,相当于xml里面的,用来告诉Spring需要扫描哪些包或类。如果不设值的话默认扫描@ComponentScan注解所在类的同级类和同级目录下的所有类,所以对于一个Spring Boot项目,一般会把入口类放在顶层目录中,这样就能够保证源码目录下的所有类都能够被扫描到。
2.4 @EnableAutoConfiguration 🚩
@EnableAutoConfiguration 源码如下:
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
这里引出了几个新的注解,@Import、@Conditional、@ConditionalOnClass、@ConditionalOnMissingBean等,@EnableAutoConfiguration 注解的核心是@Import(EnableAutoConfigurationImportSelector.class)l里面导入的EnableAutoConfigurationImportSelector.class。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
EnableAutoConfiguration.class, getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
spring-boot-autoconfigure.jar 包中的 META-INF/spring.factories 里面默认配置了很多aoto-configuration,如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
......
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
例如 WebMvcAutoConfiguration.class:
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
@Bean
@ConditionalOnMissingBean(HttpPutFormContentFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)
public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
return new OrderedHttpPutFormContentFilter();
}
......etc
}
引入这个类,相当于引入了一份webmvc的基本配置,这个类跟其它很多类一样,重度依赖于Spring Boot注释。
总的来说,@EnableAutoConfiguration完成了一下功能:
从classpath中搜寻所有的 META-INF/spring.factories 配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
2.5 @Import
相当于xml里面的,允许导入 Configuration注解类 、ImportSelector 和 ImportBeanDefinitionRegistrar的实现类,以及普通的Component类。
2.6 @Conditional
Spring Boot的强大之处在于使用了 Spring 4 框架的新特性:@Conditional注释,此注解使得只有在特定条件满足时才启用一些配置。这也 Spring Boot “智能” 的关键注解。Conditional大家族如下:
@ConditionalOnBean
@ConditionalOnClass
@ConditionalOnExpression
@ConditionalOnMissingBean
@ConditionalOnMissingClass
@ConditionalOnNotWebApplication
@ConditionalOnResource
@ConditionalOnWebApplication
以@ConditionalOnClass注解为例:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
核心实现类为OnClassCondition.class,这个注解实现类 Condition 接口:
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
2.7 总结
上面所有的注解都在做一件事:注册bean到spring容器。他们通过不同的条件不同的方式来完成:
@SpringBootConfiguration 通过与 @Bean 结合完成Bean的 JavaConfig配置;
@ComponentScan 通过范围扫描的方式,扫描特定注解注释的类,将其注册到Spring容器;
@EnableAutoConfiguration 通过 spring.factories 的配置,并结合 @Condition 条件,完成bean的注册;
@Import 通过导入的方式,将指定的class注册解析到Spring容器;
03 Spring Boot 启动流程
13.1 SpringApplication的实例化
下面开始分析关键方法:
SpringApplication.run(Application.class, args);
相应实现:
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
这里最终还是会构造一个 SpringApplication 的实例,然后运行它的run方法。
思考:
1、为什么要在静态方法里面实例化 ?
2、可不可以不实例化 ?
public SpringApplication(Object... sources) {
initialize(sources);
}
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
在构造函数中,主要做了4件事情:
3.1.1 推断应用类型是否是Web环境
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
这里通过判断是否存在 Servlet 和 ConfigurableWebApplicationContext 类来判断是否是Web环境,上文提到的 @Conditional 注解也有基于 class 来判断环境, 所以在 Spring Boot 项目中 jar包 的引用不应该随意,不需要的依赖最好去掉。
3.1.2 设置初始化器(Initializer)
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
这里出现了一个概念 - 初始化器。
先来看看代码,再来尝试解释一下它是干嘛的:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
该方法会加载所有配置的 ApplicationContextInitializer 并进行实例化,加载 ApplicationContextInitializer 是在SpringFactoriesLoader.loadFactoryNames 方法里面进行的:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
这个方法会尝试从类路径的 META-INF/spring.factories 读取相应配置文件,然后进行遍历,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer 的 value。以 spring-boot 这个包为例,它的 META-INF/spring.factories 部分定义如下所示:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
因此这两个类名会被读取出来,然后放入到集合中,准备开始下面的实例化操作:
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
初始化步骤很直观,类加载,确认被加载的类确实是 org.springframework.context.ApplicationContextInitializer 的子类,然后就是得到构造器进行初始化,最后放入到实例列表中。
因此,所谓的初始化器就是 org.springframework.context.ApplicationContextInitializer 的实现类,这个接口是这样定义的:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
ApplicationContextInitializer是一个回调接口,它会在 ConfigurableApplicationContext 容器 refresh() 方法调用之前被调用,做一些容器的初始化工作。
3.1.3. 设置监听器(Listener)
设置完了初始化器,下面开始设置监听器:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
实现方式与Initializer一样:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
同样,还是以spring-boot这个包中的 spring.factories 为例,看看相应的 Key-Value :
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
至于 ApplicationListener 接口,它是 Spring 框架中一个相当基础的接口,代码如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
这个接口基于JDK中的 EventListener 接口,实现了观察者模式。对于 Spring 框架的观察者模式实现,它限定感兴趣的事件类型需要是 ApplicationEvent 类型的子类,而这个类同样是继承自JDK中的 EventObject 类。
3.1.4. 推断应用入口类(Main)
this.mainApplicationClass = deduceMainApplicationClass();
这个方法的实现有点意思:
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
}
return null;
}
它通过构造一个运行时异常,通过异常栈中方法名为main的栈帧来得到入口类的名字。
思考:
1、获取堆栈信息的方式?
Thread.currentThread().getStackTrace();
new RuntimeException().getStackTrace();
至此,对于SpringApplication实例的初始化过程就结束了。
3.2 SpringApplication.run方法
完成了实例化,下面开始调用run方法:
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);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
这个run方法包含的内容有点多的,根据上面列举出的关键步骤逐个进行分析:
3.2.1 获取RunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
这里仍然利用了 getSpringFactoriesInstances 方法来获取实例:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
所以这里还是从 META-INF/spring.factories 中读取Key为 org.springframework.boot.SpringApplicationRunListener 的Values:
比如在spring-boot包中的定义的spring.factories:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
我们来看看这个EventPublishingRunListener是干嘛的:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
}
从类文档可以看出,它主要是负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理。至于具体的应用场景,后面用到的时候再来分析。
3.2.2 准备Environment环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
配置环境的方法:
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
所以这里实际上包含了两个步骤:
1.配置 Property Sources
2.配置 Profiles,为应用程序环境配置哪些配置文件处于active(活动)状态。
对于Web应用而言,得到的environment变量是一个StandardServletEnvironment的实例。得到实例后,会调用前面RunListeners中的environmentPrepared方法:
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
在这里,定义的广播器就派上用场了,它会发布一个 ApplicationEnvironmentPreparedEvent 事件。
那么有发布就有监听,在构建 SpringApplication 实例的时候不是初始化过一些 ApplicationListeners 嘛,其中的Listener就可能会监听ApplicationEnvironmentPreparedEvent事件,然后进行相应处理。
所以这里 SpringApplicationRunListeners 的用途和目的也比较明显了,它实际上是一个事件中转器,它能够感知到Spring Boot启动过程中产生的事件,然后有选择性的将事件进行中转。为何是有选择性的,看看它的实现就知道了:
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}
它的 contextPrepared 方法实现为空,没有利用内部的 initialMulticaster 进行事件的派发。因此即便是外部有 ApplicationListener 对这个事件有兴趣,也是没有办法监听到的。
那么既然有事件的转发,是谁在监听这些事件呢,在这个类的构造器中交待了:
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
前面在构建 SpringApplication 实例过程中设置的监听器在这里被逐个添加到了 initialMulticaster 对应的 ApplicationListener 列表中。所以当 initialMulticaster 调用 multicastEvent 方法时,这些 Listeners 中定义的相应方法就会被触发了。
3.2.3 创建Spring Context
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
AnnotationConfigEmbeddedWebApplicationContext 是个很重要的类,以后再深入分析。
思考:ssm项目中有几个上下文环境,Spring Boot中有几个上下文环境,为什么?
3.2.4 Spring Context前置处理
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
关键步骤:
配置Bean生成器以及资源加载器(如果它们非空):
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
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());
}
}
}
调用初始化器
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
这里终于用到了在创建 SpringApplication实例 时设置的初始化器了,依次对它们进行遍历,并调用initialize方法。
3.2.5 Spring Context刷新 🚩
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext)applicationContext).refresh();
}
注册关闭容器时的钩子函数的默认实现是在 AbstractApplicationContext 类中:
public void registerShutdownHook() {
if(this.shutdownHook == null) {
this.shutdownHook = new Thread() {
public void run() {
synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
AbstractApplicationContext.this.doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
如果没有提供自定义的shutdownHook,那么会生成一个默认的,并添加到Runtime中。默认行为就是调用它的doClose方法,完成一些容器销毁时的清理工作。
3.2.6 Spring Context后置处理
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
所谓的后置操作,就是在容器完成刷新后,依次调用注册的Runners。Runners可以是两个接口的实现类:
CommandLineRunner、ApplicationRunner 接口是在容器启动成功后的最后一步回调(类似开机自启动)
这两个接口有什么区别呢:
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
其实没有什么不同之处,除了接口中的run方法接受的参数类型是不一样的以外。一个是封装好的 ApplicationArguments 类型,另一个是直接的String不定长数组类型。因此根据需要选择相应的接口实现即可。
推荐阅读阿里、腾讯、百度、华为、京东最新面试题汇集
程序员都是怎么找想要的资源的?
全面了解HTTP和HTTPS
BAT大厂面试流程剖析