大家好!我是未来村村长,就是那个“请你跟我这样做,我就跟你这样做!”的村长!
未来村村长正推出一系列【To Up】文章,该系列文章重要是对Java开发知识体系的梳理,关注底层原理和知识重点。”天下苦八股文久矣?吾甚哀,若学而作苦,此门无缘,望去之。“该系列与八股文不同,重点在于对知识体系的构建和原理的探究。
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can
“just run”.
Spring被称为 J2EE的春天,是一个开源的轻量级的Java开发框架,具有控制反转(IOC)
和面向切面(AOP)
两大核心。但是使用Spring开发,我们还是需要配置Beans.xml等各种文件,如果是开发Web应用,我们还需要配置web.xml、更多的Beans.xml以及Tomcat服务器,其中存在许多固定的配置套路。
SpringBoot便是为了简化Java开发流程而诞生的。根据官方介绍,Spring Boot使您可以轻松地创建独立的、基于产品级的Spring应用程序,你只需要"run"即可。这里的run就是SpringApplication的run()方法,这是整个SpringBoot项目的启动入口,也是SpringBoot简化开发的思想体现。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class xxxApplication {
public static void main(String[] args) {
SpringApplication.run(xxxApplication.class, args);
}
}
我们将从这个Main类为出发点,分析SpringBoot的自动配置原理,设计框架,启动流程等核心原理。
在讲解xxxApplication的核心注解前,得先回顾或学习新的注解@Configuration、@Import、@Conditional。
@Configuration:作用在类上,告诉SpringBoot这是一个配置类,相当于Spring中的xml配置文件(可替换xml配置文件),本质上也是一个Component。
@Bean:作用在方法上,用于创建一个Bean对象,该Bean对象会在IoC容器启动时初始化。给容器中添加组件,相当于Spring中xml配置文件中的
@Component: 标注Spring管理的Bean,使用@Component注解在一个类上,表示将此类标记为Spring容器中的一个Bean。
SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理,用于容器初始化时通过依赖注入生成相应的Bean对象。
我们来看@Configuration的代码,我们知道Configuration实际上也是一个Component注解,该注解的属性proxyBeanMethods()的属性默认为true,意为是否通过CGLIB代理@Bean方法以强制执行bean的生命周期行为,被代理的bean将是单例化创建,即容器中只存在一个bean。虽然在Component下使用@Bean进行Bean的注册也是默认单例,但是该注册方法不会通过代理完成。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
Provides functionality equivalent to the {@code } element in Spring XML.Allows for importing {@code @Configuration} classes, {@link ImportSelector} and{@link ImportBeanDefinitionRegistrar} implementations, as well as regular component classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
Import注解对应的是配置文件中的
<import resource="bean1.xml"/>
@Import注解可以实现@Configuration类,ImportSelector和ImportBeanDefinitionRegistrar接口的实现类的导入。在Spring版本4.2以后,Import也可以导入普通类,将其注册成为Spring Bean。Import导入@Configuration类,如同将不同的beans文件导入到一个文件中。
导入ImportBeanDefinitionRegistrar接口的实现类主要用于手动注册bean到容器。
public class MyBean implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//指定bean定义信息(包括bean的类型、作用域等)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(xxxClass.class);
//注册一个bean指定bean名字(id)
beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
}
}
ImportSelector是配置类导入选择器,ImportSelector接口源码如下。ImportSelector决定可以引入哪些@Configuration类。该接口提供了selectImports方法,该方法可根据具体实现决定返回哪些配置类的全限定名,结果以字符串数组返回。
当在@Configuration
标注的Class上使用@Import
引入了一个ImportSelector
实现类后,会把实现类中返回的Class名称都定义为bean。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
一个ImportSelector
实现类通常也可能会实现各种Aware
接口,如果实现了这些Aware
接口,这些接口方法的调用会发生在selectImports
之前。比如:EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware。
@Conditional(Conditions):根据是否满足某个特定的条件创建一个特定的Bean。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
除Conditional以外,Spring还提供了以下指定条件类型的衍生Conditional:
绕了一大圈,我们再来看启动类的注解。
@SpringBootApplication
该注解是一个组合注解,可以用以下注解进行代替。
@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration
用来指定包的扫描范围,加了包扫描@ComponentScan注解后,只要标注了@Controller、@Service、@Repository、@Component注解中的任何一个,其组件都会被自动扫描,加入到容器中。
SpringBootConfiguration也是@Configuration注解,说明被注解的类是一个配置类。当我们使用@SpringBootConfiguration标记一个类时,这意味着该类提供了@Bean定义方法。 Spring容器处理配置类以为我们的应用实例化和配置bean。
@EnableAutoConfiguration的主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的Bean。自动配置通常基于项目classpath中引入的类和已定义的Bean来实现的,被自动配置的组件来自项目依赖的jar包。
详情见下节。
@EnableAutoConfiguration源码如下,其关键功能是通过@Import注解导入的ImportSelector来完成的,是自动配置的核心实现。
//启用SpringApplicationContext的自动配置,尝试猜测和配置您可能需要的bean。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
//根据类排除指定的自动配置
Class<?>[] exclude() default {};
//根据类名排除指定的自动配置
String[] excludeName() default {};
}
从其继承的接口我们可以看到,除了继承DeferredImportSelector接口以外,还继承了一系列的Aware接口。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
当使用Import注解引入AutoConfigurationImportSelector类时,其selectImport()
方法会被调用执行其实现的自动装配逻辑。在这之前,会先调用实现的Aware接口的方法。selectImports是ImportSelector接口的唯一函数方法。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//检查自动配置功能是否开启
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//封装被引入的自动配置信息
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//返回满足条件的配置类的全限定名数组
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
selectImports中又调用了getAutoConfigurationEntry()
方法,给容器批量导入相应的组件。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//加载META-INF目录下的spting.factories文件中的自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//对获得的类进行去重处理
configurations = removeDuplicates(configurations);
//获得注解中被exclude和excludeName所排除的类集合
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//从自动配置类集合中去除被排除的类
configurations.removeAll(exclusions);
//将筛选完成的配置类和排查的配置类构建为事件类,并传入监听器
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations()方法通过SpringFactoriesLoader的loadFactoryNames()
方法加载类路径中META-INF目录下spring.factories文件中针对EnableAutoConfiguration的注册配置类。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
SpringFactoriesLoader中的loadFactoryNames()方法又会调用loadSpringFactories()
方法,加载得到所有的组件。该方法调用getResources()
方法传入参数FACTORIES_RESOURCE_LOCATION ,该参数是字符串常量其值为**“META-INF/spring.factories”**。key为接口的全类名,value是对应配置值的List集合。
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//获取资源文件的位置
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
虽然127个组件自动配置启动的时候默认全部加载,但按照条件装配规则(@Conditional及其衍生注解),最终会按需配置,例如AOP组件中有@ConditionalOnProperty、@ConditionalOnClass、@ConditionalOnMissingClass等条件注解,其中@ConditionalOnClass(Advice.class)就是判断是否存在Advice类,如果没有则对应组件不会被加载。
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
@Bean
static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
return (beanFactory) -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
};
}
}
}
@SpringBootApplication注解由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三者组成。
Class that can be used to bootstrap and launch a Spring application from a Java main method.
说完了Main类的注解,我们就得看Main方法中的短短一行代码是如何执行的了。
SpringApplication.run(xxxApplication.class, args);
我们来看run方法的一系列调用情况,我们发现之前的run方法绕了个圈子。
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);
}
所以代码我们可以写成下列这种形式,我们可以看到实际上原来的代码分为两步执行,先传入primarySources参数即带有@SpringBootApplication注解的xxxApplication.class对SpringApplication进行实例化,再调用run方法来启动程序。
new SpringApplication(xxxApplication.class).run(args);
SpringApplication的作用就是该节开头引入的那句官方注解,用于从Main方法引导和启动Spring应用程序,具体的启动方法就是run方法。
我们通过SpringApplication构造方法就能知道相应的实例化流程。
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//主要的Bean来源
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//Web应用类型推断
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取系统配置引导信息【自定义】
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//加载并初始化ApplicationInitializers及相关实现类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载并初始化ApplicationListener及相关实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
根据源码,SpringApplication实例化需要经过以下流程:赋值resourceLoader和primarySources成员变量、推断Web应用类型、加载并初始化ApplicationInitializer及相关实现类、加载并初始化ApplicationListener及相关实现类、推断Main方法。
传入的参数有两个ResourceLoader和Class,前者为资源加载的接口,后者为可变参数,需要传入一个Class对象。但只有传入带有@EnableAutoConfiguration标注的Class对象才能开启自动配置。
在变量赋值完成后,进行Web应用类型的推断,这里调用了WebApplicationType的deduceFromClasspath方法进行Web应用类型的推断。该方法的推断逻辑如下:
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
ApplicationContextInitializer是IoC容器的一个回调接口,主要目的是允许用户在ConfigurableApplicationContext使用refresh()方法初始化前,对IoC容器实例做进一步设置或处理。initialize的作用为初始化给定的应用程序上下文,传入的参数限定为ConfigurableApplicationContext的子类。
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
构造器会调用getSpringFactoriesInstances方法来获取SpringFactories实例,这里传入的参数是ApplicationContextInitializer的class对象。我们从该方法的源码中可以发现,该方法调用了SpringFactoriesLoader的loadFactoryNames()来进一步调用loadSpringFactories()从而获得META-INF/spring.factories文件中注册的对应配置,这里的对应配置是ApplicationContextInitializer的实现类。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
ApplicationContext初始化器的加载就执行完毕。
接下来进行ApplicationListener的加载。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
ApplicationListener是由应用程序事件监听器实现的接口,用于在ApplicationContext管理Bean生命周期过程中,监听相应的ApplicationEvent事件,当事件发生时ApplicationListener会对事件进行具体的操作。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
ApplicationListener的加载同ApplicationContextInitializer一致,调用getSpringFactoriesInstances方法,传入ApplicationListener的Class对象。最终通过调用了SpringFactoriesLoader的loadFactoryNames()来进一步调用loadSpringFactories()从而获得META-INF/spring.factories文件中注册的对应配置,从而完成ApplicationListener的加载。
该监听器可通过传入不同的事件泛型(如ApplicationStartingEvent)从而达到对应用启动的各个流程之间进行操作增强。
Run the Spring application, creating and refreshing a new {@link ApplicationContext}.
run方法主要做了4件事:获取监听器和参数配置、打印Banner信息、创建并初始化容器、监听器发送通知。我们直接上源码。
public ConfigurableApplicationContext run(String... args) {
//计时器
long startTime = System.nanoTime();
//系统配置引导信息对应的上下文对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//设置系统配置信息headless:模拟输入输出,防止系统报错
configureHeadlessProperty();
//获得SpringApplicationRunListener数组,封装在对象listeners中:获取了当前注册的所有监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动监听
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//创建ApplicationArguments对象,获取args参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//加载环境属性配置
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = printBanner(environment);
//根据当前Spring项目类型来创建容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//准备容器
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新容器
refreshContext(context);
//刷新操作后的执行,默认为空
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
//输出启动信息日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
//通知监听器:容器启动完成
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
//通知监听器:容器准备完毕
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
//返回容器
return context;
}
SpringApplicationRunListeners listeners = getRunListeners(args);
SpringApplicationRunListener监听器从字面就可以了解,该监听器用于对SpringApplication对run方法的执行过程进行监听。我们可以自定义SpringApplicationRunListener来执行相关的操作,然后可以在resources目录下创建META-INF,在该目录下创建spring.factories写入自定义SpringApplicationRunListener全限定名。
我们来看该监听器是如何获取的,getRunListeners通过getSpringFactoriesInstances方法进行对SpringApplicationRunListener的获取。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
getSpringFactoriesInstances()虽然不能算上很熟悉,但是我们已经见过很多次了,向其传入SpringApplicationRunListener,然后通过SpringFactoriesLoader的loadFactoryNames进一步调用loadSpringFactories()从而获得META-INF/spring.factories文件中注册的对应配置。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringApplicationRunListener相应的监听类型/方法如下,会在围绕bootstrapContext和ConfigurableApplicationContext生命周期进行事件监听。
//"spring.boot.application.starting"
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass)
//"spring.boot.application.environment-prepared"
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment)
//"spring.boot.application.context-prepared"
void contextPrepared(ConfigurableApplicationContext context)
//"spring.boot.application.context-loaded"
void contextLoaded(ConfigurableApplicationContext context)
//"spring.boot.application.started"
void started(ConfigurableApplicationContext context, Duration timeTaken)
//"spring.boot.application.ready"
void ready(ConfigurableApplicationContext context, Duration timeTaken)
//"spring.boot.application.failed"
void failed(ConfigurableApplicationContext context, Throwable exception)
ApplicationArguments是应用参数,用于提供访问允许SpringApplication时的参数。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
这里传入的args来自main方法,即将main方法传入的参数封装成为Source对象,然后封装成为ApplicationArguments对象。
完成ApplicationArguments参数准备后,就开始准备可配置环境ConfigutableEnvironment,该接口的主要作用是提供当前运行环境的公开接口。
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
ApplicationContext的创建通过createApplicationContext()方法完成。
context = createApplicationContext();
该方法会根据webApplicationType,即Spring应用类型不同来进行创建。
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
这里的类型是我们在SpringApplication实例化的过程中通过deduceFromClasspath()进行判断赋值的。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
Web应用对应AnnotationConfigServletWebServerApplicationCaontext
普通应用对应AnnotationConfigApplicationContext
创建完context后就需要对容器进行准备和加载工作,通过prepareContext()来完成。
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
prepareContext()源码如下。
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置上下文配置环境
context.setEnvironment(environment);
postProcessApplicationContext(context);
//在context刷新前,执行ApplicationcontextInitializer,初始化context
applyInitializers(context);
//通知监听器,context准备完成
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
//获得ConfigutableListableBeanFactory并注册单例对象
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
//获取全部配置源,其中包含primarySource【自动配置实现注册】和sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//将sources中的Bean加载到context中
load(context, sources.toArray(new Object[0]));
//通知监听器context加载完成
listeners.contextLoaded(context);
}
准备过程分为三步:
加载过程分为五步:
refreshContext(context);
这里的refresh主要调用的是AbstractApplicationContext的refresh()方法,该方法通过Resource定位、BeanDefinition载入和注册来完成IoC容器的初始化。
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
至此,Spring上下文开启,Spring Boot正式开始运行。
SpringBoot的启动可以分为三个部分: