Springboot源码解析
0.源码环境搭建
下载地址
注意事项
1.为什么选择2.2.9.RELEASE版本
2.2.9.RELEASE以上版本采用gradle进行编译打包, 我们希望采用maven的方式
2.主pom.xml报错解决方案
标签下添加:
<disable.checks>true</disable.checks>
3.环境依赖
jdk -- jdk8+
maven -- 3.5+
在 spring-boot-project/spring-boot-parent/pom.xml文件中,依赖了一个plugin --> maven-enforcer-plugin,它有两个属性值:requireJavaVersion --> [1.8,) 以及 requireMavenVersion --> [3.5.0,)
1.准备
maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
启动类
@SpringBootApplication
public class SpringBootLearningApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootLearningApplication.class, args);
}
}
2.@SpringBootApplication
@Target(ElementType.TYPE) // 注解适用范围, TYPE表示注解可以描述在类、接口、注解、枚举上
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期
@Documented // 表示注解可以记录在javadoc中
@Inherited // 表示注解可以被子类继承
@SpringBootConfiguration // 表明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能 (spring中有很多@Enable注解, 原理就是借助@Import等相关注解向spring容器注册相关的核心Bean)
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
可以看到 @SpringBootApplication 注解上加了两个核心的自定义注解:@SpringBootConfiguration 和 @EnableAutoConfiguration,下面将介绍这两个注解的作用
2.1 @SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
总结
这个注解上加上了@Configuration注解,作用是标明当类上使用当前注解时,为主配置类
2.2 @EnableAutoConfiguration
@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 {};
}
这个注解是自动配置的核心注解,看到这个注解上又加了两个自定义注解,分别是 :@AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class),@AutoConfigurationPackage注解用来标识自动配置的包,@Import(AutoConfigurationImportSelector.class)是自动配置的核心
2.2.1 @AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) // 向spring容器中注入AutoConfigurationPackages.Registrar
public @interface AutoConfigurationPackage {
}
查看当前注解,可以看到,当前注解上添加了@Import(AutoConfigurationPackages.Registrar.class),由spring-ioc源码,我们知道@Import注解用于向spring容器注册相关bean!ConfigurationClassPostProcessor会解析主配置类(@Configuration注解的类),解析时,会委托ConfigurationClassParser进行解析,(这一部分spring-ioc源码已经分析)使用ConfigurationClassParser会递归解析主配置类中@Import注解(递归解析是指会解析注解中的注解,只要包含@Import注解,都会被解析),下面再次展示处理@Import注解的代码
ConfigurationClassParser#processImports(..)
// Collection<SourceClass> importCandidates --> 使用@Import注解引用的类
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// 1.校验非空
if (importCandidates.isEmpty()) {
return;
}
// 2.跳过
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 3.判断@Import的类是否为ImportSelector实现类
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
// 3.1 当@Import引入的类实现了ImportSelector, 进行实例化
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
// 3.2 回调Aware接口
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 3.3 如果@Import引入的类实现了ImportSelector, 但是同时实现了 DeferredImportSelector 接口(DeferredImportSelector接口是ImportSelector的子接口), 调用deferredImportSelectorHandler#handle()处理
if (selector instanceof DeferredImportSelector) {
// 这里handle方法, 将 selector 封装成 DeferredImportSelectorHolder, 添加到paser的deferredImportSelectors 列表属性中等待后续处理
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 3.4 如果只是实现了@ImportSelector, 没有实现 eferredImportSelector, 则调用其selectImports方法, 向容器中引入需要注册的bean, 并且递归调用processImports
// 本质上, @ImportSelector, 返回了A.class, B.class 相当于 @Import({A.class, B.class}), 仍然需要继续解析A.class, B.class, 判断其是否实现 @ImportSelector接口, 或者ImportBeanDefinitionRegistrar接口等!!!【这个非常重要, spring-tx就是采用这种方式】
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 4.判断@Import的类是否为ImportBeanDefinitionRegistrar实现类
// 【重点】aop的核心注解@EnableAspectJAutoProxy 中 @Import(AspectJAutoProxyRegistrar.class)
// AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口!!!
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 4.1 当@Import引入的类实现了ImportBeanDefinitionRegistrar, 进行实例化
// spring-aop、spring-transaction注解版都使用了 ImportBeanDefinitionRegistrar
// 在spring-aop中: 在主配置类上添加@EnableAspectJAutoProxy注解, @EnableAspectJAutoProxy注解上使用了
// @Import(AspectJAutoProxyRegistrar.class), AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口
// 因此这里会实例化对象 AspectJAutoProxyRegistrar
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
// 4.2 将ImportBeanDefinitionRegistrar的实现类实例化后添加到配置类的 importBeanDefinitionRegistrars属性中
// 等待后面处理
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 5.如果@Import引入的类既不是 ImportSelector实现类, 也不是 ImportBeanDefinitionRegistrar实现类
// 将其封装成 ConfigurationClass, 递归解析, 后续会创建称为普通的bean对象
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
AutoConfigurationPackages.Registrar
// 作用: 注册(org.springframework.boot.autoconfigure.AutoConfigurationPackages, BasePackages)核心bean, bean中添加一个参数:
// packageName, 这里即为主配置类所在包名
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
// 这里的packageNames是使用了@AutoConfigurationPackage注解的类所在的包名
// @SpringBootApplication注解上使用了@EnableAutoConfiguration注解,
// @EnableAutoConfiguration注解上使用了@AutoConfigurationPackage注解,
// 所以这里取的packageNames是使用@SpringBootApplication类所在的包名, 即主启动类所在包名
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
总结
@AutoConfigurationPackage的作用就是向spring容器中注册了一个bean:BasePackages
2.2.2 AutoConfigurationImportSelector
@Import解析
@Import(AutoConfigurationImportSelector.class),这个是springboot自动配置的核心,由于前面的学习,我们这里再一次总结一下@Import注解的作用(当我们的配置类上使用了@Import注解),根据@Import导入的jclass,一共可能有四种情况:
1.实现ImportBeanDefinitionRegistrar 接口
将ImportBeanDefinitionRegistrar的实现类实例化后添加到配置类的 importBeanDefinitionRegistrars属性中, 后期会调用其registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法, 向spring容器中注册相关的核心bean
我们在spring-aop, spring-mybatis源码中有实现
spring-aop中,我们在主配置类中加了@EnableAspectJAutoProxy注解,,注解中引入了AspectJAutoProxyRegistrar
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
在spring-mybatis,注解方式中,我们在主配置类中添加了@MapperScan注解,注解中@Import(MapperScannerRegistrar.class)向spring容器中导入了MapperScannerRegistrar,而MapperScannerRegistrar则实现了MapperScannerRegistrar则实现了ImportBeanDefinitionRegistrar接口
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
2.实现ImportSelector接口,但未实现DeferredImportSelector接口
将ImportSelector的实现类实例化后, 调用其String[] selectImports(AnnotationMetadata importingClassMetadata)方法, 向spring容器内导入相关的bean, 这里作用类似于@Import
我们在spring-transaction有实现
在spring-transaction中,我们在主配置类中加上了@EnableTransactionManagement注解,注解中引入了TransactionManagementConfigurationSelector,这个类实现了ImportSelector接口
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}
默认情况下,adviceMode == PROXY,所以导入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,这里相当于使用 @Import(AutoProxyRegistrar.class)和@Import(ProxyTransactionManagementConfiguration.class),我们仍然会递归处理AutoProxyRegistrar,ProxyTransactionManagementConfiguration,判断它们是否实现SelectImport接口,是否实现DefferedSelectorImport接口或是ImportBeanDefinitionRegistrar 接口等
3.实现实现ImportSelector接口,且实现DeferredImportSelector接口
这个是当前springboot中自动配置核心注解@EnableAutoConfiguration导入的AutoConfigurationImportSelector的情况
4.未实现上述所有接口
将其解析成配置类, 并处理
在spring-tx中有相关实现
在spring-tx中,由上述我们知道先通过第二种方式,向spring容器中导入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,其中 ProxyTransactionManagementConfiguration 就是普通的配置类!
DeferredImportSelector解析
由上述我们知道,当前springboot中向容器中导入了AutoConfigurationImportSelector,这个类实现了DeferredImportSelector接口,属于上述第三种情况,由上述 ConfigurationClassParser#processImports(..) 代码解析得知,这里会实例化 AutoConfigurationImportSelector,回调其相关Aware接口方法,并添加到 ConfigurationClassParser 的 deferredImportSelectors 列表属性中等待后续处理
ConfigurationClassParser#parse(Set configCandidates)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 1.解析所有的配置类
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 2.解析完所有的配置类后, 开始解析 deferredImportSelectors!!!
this.deferredImportSelectorHandler.process();
}
DeferredImportSelectorHandler#process()
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
// 1.这里 deferredImports!= null, 这里有 AutoConfigurationImportSelector 对应的 DeferredImportSelectorHolder
if (deferredImports != null) {
// 2.创建 DeferredImportSelectorGroupingHandler
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 3.调用 handler#register(DeferredImportSelectorHolder deferredImport)方法
deferredImports.forEach(handler::register);
// 4.【核心】处理, 完成自动装配
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
DeferredImportSelectorGroupingHandler##register(DeferredImportSelectorHolder deferredImport)
public void register(DeferredImportSelectorHolder deferredImport) {
// 1.获取 AutoConfigurationImportSelector 的 importGroup --> AutoConfigurationImportSelector$AutoConfigurationGroup
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
// 2.创建 DeferredImportSelectorGrouping, 添加到 groupings 中
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
// 3.添加到 configurationClasses
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
DeferredImportSelectorGroupingHandler#processGroupImports()
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// 1.这里 this.groupings.values() 即为上述创建的 DeferredImportSelectorGrouping
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 2.grouping.getImports() 特别重要, 获取需要自动装配的配置类(读取 spring.factories配置文件 并完成过滤)
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// 3.每个entry都对应着自动配置类, 这里递归处理自动配置类, 继续判断当前类是否实现 @ImportSelector、@ImportBeanDefinitionRegistry等
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
DeferredImportSelectorGrouping#getImoprts()
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 1.process --> 这个方法及其重要, 它会扫描jar包内所有的 /META-INF/spring.factories 配置文件, 读取org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ , 下的所有配置, 一共124个自动配置类的全限定类名, 并进行过滤, 过滤条件即为 自动配置类的@ConditionalOnxxx条件是否满足, 只有满足条件的才会启用!!!
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 2.getImports() --> 过滤并排序
return this.group.selectImports();
}
@ConditionalOnBean: 在当前spring容器中存在某个bean, 才会实例化当前Bean
@ConditionalOnClass: 当类路径下存在某个类的Class文件时, 才会实例化当前Bean
@ConditionalOnExpression: 当条件表达式为true时, 才会实例化当前Bean
@ConditionalOnMissingBean: 当spring容器中不存在某个bean, 才会实例化当前Bean
@ConditionalOnMissingClass: 当类路径下不存在某个类的Class文件时, 才会实例化当前Bean
@ConditionalOnProperty: 当相关属性匹配时, 才会实例化当前Bean
总结
@Import(AutoConfigurationImportSelector.class)注解的作用是, 向容器中导入了 AutoConfigurationImportSelector, 它是DefferedImportSelector的实现类, spring会实例化AutoConfigurationImportSelector, 同时实例化其内部类AutoConfigurationGroup, 调用其process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法读取 META-INF/spring.factories配置文件, 读取自动配置类, 进行过滤; 最终递归解析过滤后的自动配置类, 完成扫描和注册!!!
3.SpringApplication#run解析
3.1 SpringApplication#run(Class<?> primarySource, String... 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);
}
总结
调用run方法, 核心方法包括两部分:
第一: new SpringApplication(Class clazz) , 构造方法, 入参是启动类, 在@3.2中介绍
第二: run(args) 方法, 在@3.3中介绍
3.2 SpringApplication(Class<?>... primarySources)
public SpringApplication(Class<?>... primarySources) {
// 调用其构造方法 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
this(null, primarySources);
}
这里重点看 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
3.2.1 SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 1.赋值 resourceLoader, 一般为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 2.赋值 primarySources, 为启动类对应的Class
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 3.推断web应用类型, 有三个值: REACTIVE、SERVLET、NONE, 这里主要根据引用的jar包来判断, 见@3.2.2
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 4.getSpringFactoriesInstances(ApplicationContextInitializer.class)很重要: 这里会读取
// 所有 META-INF/spring.factories 配置文件, 获取里面所有key == org.springframework.context.ApplicationContextInitializer, 进行实例化, 一般有7个, 实例化之后存到initializer属性中, 它们的作用暂略
// 见@3.2.3
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 5.同4, 这里读取的key == ApplicationListener, 是spring事件监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 6.推断main方法的类名, 即启动类, 赋值到 mainApplicationClass 属性中
this.mainApplicationClass = deduceMainApplicationClass();
}
3.2.2 SpringApplication#deduceFromClasspath()
/**
* 推断web应用类型 -- REACTIVE、 SERVLET、 NONE(非web容器)
* @return
*/
static WebApplicationType deduceFromClasspath() {
// 1.使用类加载器加载对应的类
// 能加载到: org.springframework.web.reactive.DispatcherHandler
// 且不能加载到: org.springframework.web.servlet.DispatcherServlet, org.glassfish.jersey.servlet.ServletContainer
// 满足以上三个条件, 即为REACTIVE
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 2.加载不到 javax.servlet.Servlet 或 org.springframework.web.context.ConfigurableWebApplicationContext
// 为NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 3.不满足以上条件, 则为SERVLET, 这是springmvc环境中的
return WebApplicationType.SERVLET;
}
3.2.3 SpringApplication#getSpringFactoriesInstances(Class type)
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) {
// 1.获取类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 2.读取 META-INF/spring.factories配置文件中, 根据key取相关的value
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 3.实例化读取的全限定类名的实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 4.排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
总结
SpringApplication构造方法中, 主要:
1.推断当前spring应用的类型, 类型有三个: reactive, servlet, none, 推断的依据是根据当前应用所引用的spring相关jar包中的类, 如引用springmvc相关jar包, 则为servlet
2.从 META-INF/spring.factories 配置文件中读取两个key对应的value:
ApplicationContextInitializer 和 ApplicationListener
3.推断main函数的启动类
4.将以上信息存储到SpringApplication对应的属性上
3.3 SpringApplication#run(String... args)
// 创建应用并启动
public ConfigurableApplicationContext run(String... args) {
// 1.记录运行时间, 暂略
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 2.获取SpringApplicationRunListeners监听器
// 这里也是通过读取 META-INF/spring.factories配置文件, 读取 key == org.springframework.boot.SpringApplicationRunListener
// 对应的value, 这里只有一个
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3.启动监听器 -- 这里的监听器是事件发布器
// 这里会发布 ApplicationStartingEvent 应用启动等相关事件
// 监听器监听事件后会进行对应处理, 监听器是在构造器中读取spring.factories配置文件, 进行实例化并存储在当前SpringApplication中的
// 如: 这里有一个 ConfigFileApplicationListener监听到此事件后, 会读取主配置文件 application.yml文件等
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 4.构建 ConfigurableEnvironment, 这里会根据当前应用的类型创建不同类型的Environment对象
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 5.打印spring图标
Banner printedBanner = printBanner(environment);
// 6.创建 ApplicationContext
// 根据当前应用类型创建不同类型的ApplicationContext的子类:
// servlet --> AnnotationConfigServletWebServerApplicationContext
// reactive --> AnnotationConfigReactiveWebServerApplicationContext
// none --> AnnotationConfigApplicationContext(这个是我们前期spring-ioc源码介绍中的核心类)
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 7.ApplicationContext 准备阶段, 见@3.3.1
// 这里将启动类封装成BeanDefinition, 注册到spring容器的 beanDefinitionMap 中
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 8.【核心方法】refresh, 这里解析就是spring-ioc中的启动核心部分, 启动类上加了@SpringBootApplication中有@Configuration注解, 所以这里启动类会被封装成 主配置类 ConfigurationClass 进行解析!
refreshContext(context);
// 9.运行时间记录停止
afterRefresh(context, applicationArguments);
// 10.发布容器启动完成事件
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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.3.1 SpringApplication#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 1.设置 environment
context.setEnvironment(environment);
// 2.设置beanFactory的 ConversionService 属性
postProcessApplicationContext(context);
// 3.调用前期从 spring.factories 配置文件中读取的 ApplicationContextInitializer 实例化后的 initialize方法
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 4.创建并注册启动类的BeanDefinition到spring容器中BeanDefinitionMap中
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
总结
这里主要跟spring-ioc源码类似, 有两点不同:
1.这里的主配置类@Configuration对应的类为启动类
2.启动类中的@SpringBootApplication注解中引用了 AutoConfigurationImportSelector , 这个类实现了DefferedImportSelector接口, 完成了META-INF/spring.factories中自动配置类的扫描和初始化!
4.SpringBoot Starter机制
4.1 概念
springboot中的starter是一种非常重要的机制, 能够抛弃以前繁杂的配置, 将其统一集成进starter, 应用者只需要在maven中引入starter依赖, springboot就能自动扫描到要加载的信息并启动相应的默认配置。springboot会自动通过classpath路径下的类发现需要的bean, 并注册进ioc容器
4.2 自定义starter
springboot内置starter:spring-boot-starter-xxx
自定义starter:xxx-spring-boot-starter
5.内嵌tomcat
5.1 spring-boot-starter-web
我们在项目中引入了 spring-boot-starter-web 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
查看 spring-boot-starter-web 包,我们知道 spring-boot-starter-xxx 包中一般不会有java代码,都是引入spring和相关第三方包,并通过spring-boot自动配置机制,完成相关核心bean的注入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- spring-boot-starter-web 默认采用tomcat作为web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
5.2 Tomcat内嵌原理
5.2.1 说明
由之前的学习我们知道,springboot自动配置原理中,在ApplicationContext启动时,会调用核心方法refresh(),会使用DefferedImportSelector的实现类 --> AutoConfigurationImportSelector及其内部类 AutoConfigurationGroup 去读取所有依赖jar包中的 META-INF/spring.factories 配置文件,获取key == org.springframework.boot.autoconfigure.EnableAutoConfiguration的实现类,通过实现类中的@ConditionalOnxxx进行过滤,判断当前类是否需要注册到spring中!!!
其中就包含了 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration和org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
5.2.2 ServletWebServerFactoryAutoConfiguration
Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
........
}
查看其@Import注解,它向容器中导入了 ServletWebServerFactoryConfiguration的内部类 EmbeddedTomcat、EmbeddedJetty和EmbeddedUndertow三个servlet容器
5.2.3 ServletWebServerFactoryConfiguration
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedJetty {
@Bean
JettyServletWebServerFactory JettyServletWebServerFactory(
ObjectProvider<JettyServerCustomizer> serverCustomizers) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow {
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.getDeploymentInfoCustomizers()
.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
}
通过上述可以看到具体使用哪个servlet容器,也是通过@ConditionalOnxxx注解实现的,因为spring-boot-starter-web中默认使用了 tomcat,所以这里向容器中注入了 TomcatServletWebServerFactory,这个类是tomcatserver的工厂类,用于创建tomcat实例
5.3.4 DispatcherServletAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 配置加载顺序
@Configuration(proxyBeanMethods = false) // 不需要进行 cglib 代理
@ConditionalOnWebApplication(type = Type.SERVLET) // Servlet 应用的类型才注入当前 Bean
@ConditionalOnClass(DispatcherServlet.class) // 存在 DispatcherServlet 这个类才注入当前 Bean
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) // 配置加载顺序, 需要在ServletWebServerFactoryAutoConfiguration加载之后
public class DispatcherServletAutoConfiguration {
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) // 为 DispatcherServlet 定义一个 RegistrationBean 对象,目的是往 ServletContext 上下文中添加 DispatcherServlet
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) // 需要存在名称为 `dispatcherServlet` 类型为 DispatcherServlet 的 Bean
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
...省略...
}
这是另一个自动配置的核心类, 可以看到当同时满足 @ConditionalOnWebApplication(type = Type.SERVLET), @ConditionalOnClass(DispatcherServlet.class) 这两个条件后, 就会向spring中注册DispatcherServletAutoConfiguration, 同时其中有两个内部类, DispatcherServletConfiguration, DispatcherServletRegistrationConfiguration, 这两个内部类有两个@Bean方法, 它们分别向spring容器中注册了 DispatcherServlet、 DispatcherServletRegistrationBean, DispatcherServletRegistrationBean负责将DispatcherServlet注册到springmvc容器中
总结
1.首先我们在项目中导入 spring-boot-starter-web , 它引用了tomcat相关jar
2.在SpringApplication.run()方法中, 会根据当前应用的类型, 创建对应的ApplicationContext上下文, 并调用其refresh()方法, 由前面分析我们知道, refresh()方法中借助 AutoConfigurationImportSelector 完成了自动配置, 其核心是读取所有jar中的 META-INF/spring.factories 配置文件, 并根据@ConditionalOnxxx 注解判断当前spring容器是否需要注入当前bean, 我们在引用spring-boot-starter-web后, 会向spring容器中引入 ServletWebServerFactoryAutoConfiguration, 在这个类上又有@Import注解, 根据具体依赖的servlet容器, 判断具体引入的ServerFactory, 默认会向容器中导入 TomcatServletWebServerFactory, 它用于创建tomcat实例
3.同时, 向spring容器中注册了 DispatcherServlet 和 DispatcherServletRegistrationBean, 其中DispatcherServletRegistrationBean实现了 ServletContextInitializer 接口
5.3 tomcat启动
5.3.1 说明
由上述,我们知道调用SpringApplication#run方法时会创建ApplicationContext对象(这里的ApplicationContext实现类为:AnnotationConfigServletWebServerApplicationContext),并向容器中导入TomcatServletWebServerFactory,用于创建tomcat容器,那么tomcat容器何时创建并启动?
5.3.2 SpringApplication#run(String... args)
// 创建应用并启动
public ConfigurableApplicationContext run(String... args) {
// 1.记录运行时间, 暂略
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 2.获取SpringApplicationRunListeners监听器
// 这里也是通过读取 META-INF/spring.factories配置文件, 读取 key == org.springframework.boot.SpringApplicationRunListener
// 对应的value, 这里只有一个
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3.启动监听器 -- 这里的监听器是事件发布器
// 这里会发布 ApplicationStartingEvent 应用启动等相关事件
// 监听器监听事件后会进行对应处理, 监听器是在构造器中读取spring.factories配置文件, 进行实例化并存储在当前SpringApplication中的
// 如: 这里有一个 ConfigFileApplicationListener监听到此事件后, 会读取主配置文件 application.yml文件等
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 4.构建 ConfigurableEnvironment, 这里会根据当前应用的类型创建不同类型的Environment对象
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 5.打印spring图标
Banner printedBanner = printBanner(environment);
// 6.创建 ApplicationContext
// 根据当前应用类型创建不同类型的ApplicationContext的子类:
// servlet --> AnnotationConfigServletWebServerApplicationContext (springmvc使用的)
// reactive --> AnnotationConfigReactiveWebServerApplicationContext
// none --> AnnotationConfigApplicationContext(这个是我们前期spring-ioc源码介绍中的核心类)
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 7.ApplicationContext 准备阶段, 见@3.3.1
// 这里将启动类封装成BeanDefinition, 注册到spring容器的 beanDefinitionMap 中
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 8.【核心方法】refresh, 这里解析就是spring-ioc中的启动核心部分, 启动类上加了@SpringBootApplication中有@Configuration注解, 所以这里启动类会被封装成 主配置类 ConfigurationClass 进行解析!, 见@5.3.3
refreshContext(context);
// 9.运行时间记录停止
afterRefresh(context, applicationArguments);
// 10.发布容器启动完成事件
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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;
}
5.3.3 AnnotationConfigServletWebServerApplicationContext架构图
5.3.4 SpringApplication#refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
// 这里的context --> AnnotationConfigServletWebServerApplicationContext
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
SpringApplication#refresh
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 这里的ApplicationContext --> AnnotationConfigServletWebServerApplicationContext
((AbstractApplicationContext) applicationContext).refresh();
}
ServletWebServerApplicationContext#refresh
public final void refresh() throws BeansException, IllegalStateException {
try {
// 这里会调用 ioc 源码解析中的核心类 AbstractApplicationContext#refresh
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
AbstractApplicationContext#refresh
这个方法我们在spring-ioc中已经解析过,这里重点看onRefresh()方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
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.
// 这里采用模板方法设计模式, 由子类 AnnotationConfigServletWebServerApplicationContext实现, 该类继承了 ServletWebServerApplicationContext, 具体由其实现, 见@5.3.5
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();
}
}
}
5.3.5 ServletWebServerApplicationContext#onRefresh
protected void onRefresh() {
// 1.调用父类方法, 一般为空实现
super.onRefresh();
try {
// 2.创建server, 这里会创建servlet容器!见@5.3.6
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
5.3.6 ServletWebServerApplicationContext#createWebServer
private void createWebServer() {
// 1.这里 webServer == null
WebServer webServer = this.webServer;
// 2.这里 servletContext == null
ServletContext servletContext = getServletContext();
// 3.进入
if (webServer == null && servletContext == null) {
// 4.获取WebServerFactory, 这里会从spring容器中获取, 由前面学习我们知道, 在 ServletWebServerFactoryAutoConfiguration 中默认会向spring容器中注入 TomcatServletWebServerFactory, 用来创建 tomcat 容器, 所以这里的factory == TomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 5.这里分两步, 都很重要:
// 5.1 getSelfInitializer() 见@5.3.7, 这里使用了jdk8新特性, 方法引用, 即返回一个匿名内部类对象
// 5.2 factory#getWebServer 获取web容器, 并启动, 见@5.3.8
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
5.3.7 ServletWebServerApplicationContext#getSelfInitializer
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
// 调用的是下面的方法
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
// 1.将 servletContext 赋值到 WebApplicationContext属性上
prepareWebApplicationContext(servletContext);
// 2.ServletContextScope处理, 暂略
registerApplicationScope(servletContext);
// 3.将 ServletContext 及其相关属性对象注入到spring容器中
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 4.获取 ServletContextInitializer, 调用其startup方法
// 这里会获取 之前注入的 DispatcherServletRegistrationBean, 并调用其onStartup方法, 向ServletContext中注入 DispatcherServlet
// 以及其他 Servlet 和 Filter 等, 完成 Servlet, Filter 等的注册!!!
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
ServletWebServerApplicationContext#getServletContextInitializerBeans()
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
ServletContextInitializerBeans构造方法
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
// 1.initializerTypes == ServletContextInitializer
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 2.找到 spring 容器中所有 `ServletContextInitializer` 类型的 Bean, 并将这些信息添加到 `seen` 和 `initializers` 集合中
addServletContextInitializerBeans(beanFactory);
// 3.找到 spring 容器中所有 Servlet or Filter or EventListener 类型的 Bean, 适配成 RegistrationBean 对象, 并将这些信息添加到 `seen` 和 `initializers` 集合中
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
5.3.8 TomcatServletWebServerFactory#getWebServer
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1.创建tomcat对象
Tomcat tomcat = new Tomcat();
// 2.tomcat相关配置
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
// 3.创建 TomcatWebServer, 在其构造器方法中, 会调用init方法, 完成 tomcat容器的启动
// 同时也会回调 initializers 的 onStartup方法, @5.3.7中的方法
return getTomcatWebServer(tomcat);
}
评论