5w字SpringBoot源码分析
Posted 结构化思维wz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5w字SpringBoot源码分析相关的知识,希望对你有一定的参考价值。
Spring Boot源码分析
文章目录
SpringBoot版本-2.X
启动类分析
@SpringBootApplication
标注在某个类上说明这个类是 SpringBoot 的主配置类, SpringBoot 就应该运行这个类的main方法来启动 SpringBoot 应用;
@SpringBootApplication
public class StudyProjectApplication
public static void main(String[] args)
SpringApplication.run(StudyProjectApplication.class, args);
当我们点进去后发现:这是一个集成注解,为什么它能集成这么多的注解的功能呢?是在于它上面的 @Inherited
注解, @Inherited
表示自动继承注解类型。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters =
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) )
public @interface SpringBootApplication
/**
* 自动装配要排除的类,功能来自于 @EnableAutoConfiguration
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default ;
/**
* 自动装配要排除的类名,功能来自于 @EnableAutoConfiguration
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default ;
/**
* 配置扫描的包,功能来自于 @ComponentScan
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default ;
/**
* 配置扫描的class,该class所在的包都会被扫描,功能来自于 @ComponentScan
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default ;
/**
* 是否启用 @Bean 方法代理,功能来自于 @Configuration
*/
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
@SpringBootApplication
是一个组合注解,包含了 @SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
三个注解的功能;@SpringBootApplication
中也提供了一些配置属性,而这些属性来自于以上三个注解。
@SpringBootConfiguration
我们点进注解发现:它是 springboot
的配置类,标注在某个类上,表示这是一个 springboot
的配置类。
这个注解比较简单,上面标记了 @Configuration
,然后是一个属性 proxyBeanMethods()
,它来自于 @Configuration
。因此,@SpringBootConfiguration
并没有做什么,仅仅只是将 @Configuration
使用了 @Configuration
的功能。
@EnableAutoConfiguration
@EnableAutoConfiguration
主要 用来开启自动装配功能,在第二章SpringBoot自动装配原理中详细说明。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 将被 @SpringBootApplication 标记的类所在的包,包装成 BasePackages,然后注册到 spring 容器中;
@AutoConfigurationPackage
// 将当前项目支持的自动配置类添加到 spring 容器中;
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 可自行定义排除自动装配的类
*/
Class<?>[] exclude() default ;
/**
* 可自行定义排除自动装配的类名
*/
String[] excludeName() default ;
从代码中可以看到,
- 该注解组合了
@AutoConfigurationPackage
注解的功能,该注解用来指定自动装配的包; - 该注解通过
@Import
注解引入了一个类AutoConfigurationImportSelector
,这个类是自动装配的关键; - 该注解提供了两个配置,用来排除指定的自动装配类,可以根据类来排除 (
Class
对象),也可以根据类名 (包名.类名
) 排除。
@ComponentScan
这个注解想必大家已经很熟悉了,它指定了包扫描路径,如果不指定,就扫描所在类的包。
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) )
TypeExcludeFilter: 这个类表示在进行包扫描时,可以排除一些类。
AutoConfigurationExcludeFilter:用来排除自动配置类,也就是说,spring 在进行包扫描时,不会扫描自动配置类。核心源码如下:
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware
private ClassLoader beanClassLoader;
private volatile List<String> autoConfigurations;
@Override
public void setBeanClassLoader(ClassLoader beanClassLoader)
this.beanClassLoader = beanClassLoader;
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException
// isConfiguration(...):当前类是否被 @Configuration 标记
// isAutoConfiguration(...):当前类是否为自动配置类
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
private boolean isConfiguration(MetadataReader metadataReader)
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
private boolean isAutoConfiguration(MetadataReader metadataReader)
// 获取所有的自动配置类,然后判断当前类是否存在于其中
return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
protected List<String> getAutoConfigurations()
if (this.autoConfigurations == null)
this.autoConfigurations = SpringFactoriesLoader
.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
return this.autoConfigurations;
我们主要看 match(...)
方法,它的匹配的类为:
- 被
@Configuration
标记; - 是自动配置类。(从
isAutoConfiguration(...)
可以看到,在判断是否为自动配置类上,springboot 先使用SpringFactoriesLoader
加载所有配置类,然后再判断传入的类是否为其中之一。)
满足以上两个条件,spring 就不会对其进行扫描处理。
SpringBoot启动流程
@SpringBootApplication
public class StudyProjectApplication
public static void main(String[] args)
SpringApplication.run(StudyProjectApplication.class, args);
我们进入这个run方法:
public class SpringApplication
...
// primarySource 就是我们传入的 StudyProjectApplication.class,
// args 就是 main() 方法的参数
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args)
// 将 primarySource 包装成数组,继续调用 run(...) 方法
return run(new Class<?>[] primarySource , args);
// primarySources 就是我们传入的 StudyProjectApplication.class 包装成的数组,
// args 就是 main() 方法的参数
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args)
// 核心代码
return new SpringApplication(primarySources).run(args);
...
分析关键代码: return new SpringApplication(primarySources).run(args);
这块代码需要拆开来看,可以拆成以下两部分:
- 构造方法:
SpringApplication#SpringApplication(Class<?>...)
- 实例方法:
SpringApplication#run(String...)
看来,这两个方法就是 springboot 的启动所有流程了,接下来我们就来分析这两个方法。
1. 准备SpringApplication
创建SpringApplication对象
/**
* 这里就最终调用的构造方法了
* resourceLoader 为 null
* primarySources 为 StudyProjectApplication.class
*/
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
// 1. 将传入的resourceLoader设置到成员变量,这里的值为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 2. 将传入的primarySources设置到成员变量,这里的值为 StudyProjectApplication.class
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 3. 当前的 web 应用类型,REACTIVE,NONE,SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 4. 设置初始化器,getSpringFactoriesInstances:从 META-INF/spring.factories 中获取配置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 5. 设置监听器,getSpringFactoriesInstances:从 META-INF/spring.factories 中获取配置
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 6. 返回包含main()方法的class
this.mainApplicationClass = deduceMainApplicationClass();
获取当前 web 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
WebApplicationType.deduceFromClasspath()
方法是用来推断当前项目是什么类型的,代码如下:
public enum WebApplicationType
// 不是 web 应用
NONE,
// servlet 类型的 web 应用
SERVLET,
// reactive 类型的 web 应用
REACTIVE;
...
private static final String[] SERVLET_INDICATOR_CLASSES =
"javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" ;
private static final String WEBMVC_INDICATOR_CLASS
= "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS
= "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS
= "org.glassfish.jersey.servlet.ServletContainer";
static WebApplicationType deduceFromClasspath()
// classpath 中仅存在 WEBFLUX 相关类
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null))
return WebApplicationType.REACTIVE;
// classpath 不存在 SERVLET 相关类
for (String className : SERVLET_INDICATOR_CLASSES)
if (!ClassUtils.isPresent(className, null))
return WebApplicationType.NONE;
// 默认 web 类型为 SERVLET
// 也就是说,同时存在 WEBFLUX 与 SERVLET 相关类,最终返回的是 SERVLET
return WebApplicationType.SERVLET;
...
可以看到,springboot 定义了三种项目类型:NONE
(不是 web 应用)、SERVLET
(servlet
类型的 web 应用)、REACTIVE
(reactive
类型的 web 应用),WebApplicationType.deduceFromClasspath()
的执行流程如下:
- 如果
classpath
中仅存在WEBFLUX
相关类,则表明当前项目是reactive
类型的 web 应用,返回; - 如果
classpath
中不存在SERVLET
相关类,则表明当前项目不是 web 应用,返回; - 如果以上条件都不满足,则表明当前项目是
servlet
类型的 web 应用。
由于 demo 引用了 spring-boot-starter-web
相关依赖,因此当前项目是 servlet
类型的 web 应用。
设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
这行代码分为两部分:
- 获取
ApplicationContextInitializer
:getSpringFactoriesInstances(ApplicationContextInitializer.class)
- 设置初始化器:
setInitializers(...)
public class SpringApplication
...
// type 为 ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type)
return getSpringFactoriesInstances(type, new Class<?>[] );
/**
* type 为 ApplicationContextInitializer.class
* parameterTypes 为 ew Class<?>[]
* args 为 null
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
ClassLoader classLoader = getClassLoader();
// 从 META-INF/spring.factories 加载内容
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化,使用的反射操作
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序,比较的是 @Order 注解,或实现的 Orderd 接口
AnnotationAwareOrderComparator.sort(instances);
return instances;
...
先从 META-INF/spring.factories
获取内容,然后使用反射进行实例化,进行排序后再返回。那么最终会有多少个 ApplicationContextInitializer
加载进来呢?
通过调试,发现一共有 7 个:对这 7 个 ApplicationContextInitializer
,说明如下:
ConfigurationWarningsApplicationContextInitializer
:报告 IOC 容器的一些常见的错误配置ContextIdApplicationContextInitializer
:设置 Spring 应用上下文的 IDDelegatingApplicationContextInitializer
:加载application.properties
中context.initializer.classes
配置的类RSocketPortInfoApplicationContextInitializer
:将RSocketServer
实际使用的监听端口写入到Environment
环境属性中ServerPortInfoApplicationContextInitializer
:将内置 servlet 容器实际使用的监听端口写入到Environment
环境属性中SharedMetadataReaderFactoryContextInitializer
:创建一个SpringBoot
和ConfigurationClassPostProcessor
共用的CachingMetadataReaderFactory
对象ConditionEvaluationReportLoggingListener
:将ConditionEvaluationReport
写入日志
获取到 ApplicationContextInitializer
,我们再来看看 setInitializers(...)
方法:
public class SpringApplication
...
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers)
this.initializers = new ArrayList<>(initializers);
...
这是一个标准的 setter
方法,所做的就只是设置成员变量。
注意: 这里会把项目中所有的spring.factories中的key都存到内存里,以便后面快速找到对应的key
设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
从形式上看,同 Initializer
一样,也是先从 META-INF/spring.factories
中加载 ApplicationListener
,然后添加到成员变量中,这里我们直接看能获取到哪些 listener
:
这些Listener的作用是:
ClearCachesApplicationListener
:应用上下文加载完成后对缓存做清除工作ParentContextCloserApplicationListener
:监听双亲应用上下文的关闭事件并往自己的子应用上下文中传播CloudFoundryVcapEnvironmentPostProcessor
:对CloudFoundry
提供支持FileEncodingApplicationListener
:检测系统文件编码与应用环境编码是否一致,如果系统文件编码和应用环境的编码不同则终止应用启动AnsiOutputApplicationListener
:根据spring.output.ansi.enabled
参数配置AnsiOutput
ConfigFileApplicationListener
:从常见的那些约定的位置读取配置文件DelegatingApplicationListener
:监听到事件后转发给application.properties
中配置的context.listener.classes
的监听器ClasspathLoggingApplicationListener
:对环境就绪事件ApplicationEnvironmentPreparedEvent
和应用失败事件ApplicationFailedEvent
做出响应LoggingApplicationListener
:配置LoggingSystem
,使用logging.config
环境变量指定的配置或者缺省配置LiquibaseServiceLocatorApplicationListener
:使用一个可以和SpringBoot
可执行 jar 包配合工作的版本替换LiquibaseServiceLocator
BackgroundPreinitializer
:使用一个后台线程尽早触发一些耗时的初始化任务
再来看看 SpringApplication#setListeners
:
public void setListeners(Collection<? extends ApplicationListener<?>> listeners)
this.listeners = new ArrayList<>(listeners);
推导主应用程序类
this.mainApplicationClass = deduceMainApplicationClass();
所谓主类,就是包含 main(String[])
,也就是当前 spring 应用的启动类,SpringApplication#deduceMainApplicationClass
代码如下:
private Class<?> deduceMainApplicationClass()
try
// 获取调用栈
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 遍历调用栈,找 到main方法
for (StackTraceElement stackTraceElement : stackTrace)
if ("main".equals(stackTraceElement.getMethodName())RabbitMQ从概念到使用从Docker安装到RabbitMQ整合Springboot1.5w字保姆级教学