本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net
@SpringBootApplication 详解
1 . 简介
在 Spring Boot 的学习中难免需要接触源码,而入手及时从 Spring Boot 项目启动类开始入手。项目启动类非常简单,仅仅存在一个注解 @SpringBootApplication 以及一个运行参数为被该注解标注类 run 函数。
@SpringBootApplication
public class BiuApplication {
public static void main(String[] args) {
SpringApplication.run(BiuApplication.class, args);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration//引入EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...//具体参数暂时忽略
}
对于该启动类的分析,就从这个 Spring Boot 的核心注解开始入手。
2 . 核心注解 @SpringBootApplication
字面分析,这个注解是标注一个 Spring Boot 应用。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {...}
进入到这个注解后,可以发现该注解由四个元注解,以及其他三个注解组成分别是:@SpringBootConfiguration、
@EnableAutoConfiguration(自动配置有关的核心注解,主要分析)、
@ComponentScan(包扫描)(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
2.1 Spring 的配置类 @SpringBootConfiguration
字面分析,这是一个 Spring Boot 的配置类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)//自动配置的引入选择器实现
public @interface EnableAutoConfiguration {...}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {...}
从他的源码来看,除了元注解之外,它仅仅被@Configuration注解所标注,那么可以理解@SpringBootConfiguration为他仅仅就是一个配置类。
@Configuration 源码
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
再分析 @Configuration 的源码,该注解为 Spring 中的配置类注解,其中被 @Component 标注为 Spring 组件,意味着他被注册到 IOC 容器。
因此,套用官方文档的答案:@SpringBootConfiguration 表示一个类提供 Spring Boot 应用程序 @Configuration。可以用作 Spring 标准 @Configuration 注释的替代方法,以便可以自动找到配置(例如在测试中)。
2.2 开启自动配置 @EnableAutoConfiguration !!!
这个注解是 Spring Boot 的自动装配的核心。
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//BeanDefinitionRegistry registry 其中放方法boolean containsBeanDefinition(String beanName);是为了判断是否已经注册了`AutoConfigurationPackages`的类路径所对应的`bean(AutoConfigurationPackages)`
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition =(BasePackagesBeanDefinition)registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
除了四个元注解,这个注解被两个注解所标注:
@AutoConfigurationPackage、
@Import(AutoConfigurationImportSelector.class)
那么我们先直接往下分析
2.2.1 自动配置包 @AutoConfigurationPackage
public String[] selectImports(AnnotationMetadata annotationMetadata) {//selectImports方法会被springboot自动调用,从而得到他返回的全类名的字符串数组,然后把对应类的bean对象注入到ioc容器中
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
从注解来看,@AutoConfigurationPackage中使用注解@Import(@Import: 的作用) 导入了 AutoConfigurationPackages.Registrar.class 到容器中,那么来分析这个类,进入到这个内部类 Regisrar:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())//load 加载的意思
.getCandidates();
Assert.notEmpty(configurations,
"No auto configuration classes found in "
+ "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)//如果当前环境存在DispatchServlet类,则注入,否则不注入
//注意 DispatchServlet类 是引用web启动依赖后会有的
public class DispatcherServletAutoConfiguration {
...
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
...
}
该类引入的重点在于方法**registerBeanDefinitions():**
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
首先先分析方法体中所调用的方法register()的第二个参数
PackageImports(metadata).getPackageNames().toArray(new String[0])
进入到类 PackageImports 的构造方法:
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
在这个构造方法中将元数据即启动类AnnotationMetadata metadata经过处理
- 获取标签注解信息,注解信息里面的
basePackages和basePackageClasses是否有数据。
basePackages、 basePackageClasses为注解@AutoConfigurationPackage中的属性。
- 如果没有数据则获取注解所在的类的名字目录,放到 List 中
获得packageNames属性也就是启动类所在的包。
回到Registrar中的registerBeanDefinitions()方法中register()方法的第二个参数即为启动类所在的包的名称,并且使用数组来进行表示。
在分析**register()**方法,register() 源码如下:
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//BeanDefinitionRegistry registry 其中放方法boolean containsBeanDefinition(String beanName);是为了判断是否已经注册了`AutoConfigurationPackages`的类路径所对应的`bean(AutoConfigurationPackages)`
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition =(BasePackagesBeanDefinition)registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
这个方法的if语句为判断registry这个参数中是否已经注册了AutoConfigurationPackages的类路径所对应的bean(AutoConfigurationPackages)。如若已经被注册,则把上面分析的第二个参数所获取的包(启动类所在的包的名称)添加到这个bean的定义中。如若没有,则注册这个bean并且把包名设置到该bean的定义中。
小结:@AutoConfigurationPackage就是添加该注解的类所在的包作为自动配置包进行管理。他的实现就是依赖于工具类 AutoConfigurationPackages 中的内部类 Registrar 对所标注的包进行注册。
2.2.2 导入 自动配置导入选择器 @Import(AutoConfigurationImportSelector.class)
这个@import使用了 2.2.1 中 @import 的用法中的通过 ImportSelector 方式导入的类,所以我们进入到该类,直接找到selectImports方法。在这个用法中,所返回的字符串数组为所有的将要被导入的类的全类名。那么知道这个方法是做什么的,就开始分析这个方法。
再分析**selectImports**方法,selectImports()源码如下:
public String[] selectImports(AnnotationMetadata annotationMetadata) {//selectImports方法会被springboot自动调用,从而得到他返回的全类名的字符串数组,然后把对应类的bean对象注入到ioc容器中
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
从 return 开始分析,autoConfigurationEntry 自动配置实体中 List 的属性 configurations 将被返回。autoConfigurationEntry 是通过方法**getAutoConfigurationEntry()**获得的,那么就进入到这个方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
根据 return 所返回的内容,返回的是一个使用属性 configurations 所生成的自动配置实体,configurations 是使用**getCandidateConfigurations()**获取候选配置所得到的。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())//load 加载的意思
.getCandidates();
Assert.notEmpty(configurations,
"No auto configuration classes found in "
+ "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
从以上源码可以发现,在springboot3之后,已完全放弃对META-INF/spring.factories的读取。而是从传的AutoConfiguration.class取的类名称。即org.springframework.boot.autoconfigure.AutoConfiguration。拼接之后 ,META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(.imports 配置文件)。(2.7 升级到 3.0 后的变化)
读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
DispatcherServletAutoConfiguration配置类中还有配置类DispatcherServletConfiguration,其中还有方法dispatcherServlet(WebMvcProperties webMvcProperties)添加了@bean注解springboot会继续解析,直到把@Bean注解的方法都解析到,然后执行这些方法,把返回值注入到ioc容器中,因此自动配置的核心在配置文件里
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)//如果当前环境存在DispatchServlet类,则注入,否则不注入
//注意 DispatchServlet类 是引用web启动依赖后会有的
public class DispatcherServletAutoConfiguration {
...
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
...
}
2.2.3 总结!!!
@SpringBootApplication是一个组合注解,其中有@EnableAutoConfiguration也是一个组合注解,@EnableAutoConfiguration有 `@Import(AutoConfigurationImportSelector.class),导入了AutoConfigurationImportSelector.class类,AutoConfigurationImportSelector.class实现了selectImports方法,经过层层调用最终读取一个配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(注意,springboot2.7前读取的是spring.factories文件,2.7-3.0 兼容两个,3.0 之后只有.imports) 这个配置文件中写了一堆的全类名,其中有一个是org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,它是完成DispatcherServlet这个对象的自动注入的,DispatcherServletAutoConfiguration类中有@AutoConfiguration标明是一个自动配置类,@ConditionalOnClass(DispatcherServlet.class)用来去设置bean注册的条件(如果环境里有DispatcherServlet.class这个配置类,DispatcherServletAutoConfiguration这个自动配置类就生效,否则就不生效,注意引入web启动依赖就有DispatcherServlet.class这个配置类),因此说引入了web启动依赖springboot就自动注入了一个DispatcherServlet。