@SpringBootApplication自动配置原理剖析
自动配置: 根据我们添加的依赖,会自动将一些配置类的bean注册进ioc容器中,可以使用@Autowired
或者@Resource
等注解来使用它。
1.1 SpringBootApplication
Spring Boot项目创建完成会默认生成一个Application的入口类(启动类),命名规则artifactId(项目名)+Application
@SpringBootConfiguration //标明该类为配置类
@EnableAutoConfiguration //启动自动配置功能
@ComponentScan( //启动包扫描
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
@SpringBootApplication
是一个"三体"结构,实际上它是一个复合Annotation
(注解):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
//这个注解表示 @SpringBootApplication 只能用于类级别的元素(即 ElementType.TYPE)。
@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类,接口,注解或枚举中
//这个注解表示 @SpringBootApplication 在运行时仍然有效,因此可以通过反射机制访问到。
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
//这个注解表示 @SpringBootApplication 的相关信息会被包含在 Java 文档中。
@Documented
//这个注解表示如果一个父类被 @SpringBootApplication 注解,那么它的子类也会继承这个注解。
@Inherited
//这个注解等同于 @Configuration 注解,表示这是一个配置类,可以被用来声明 Bean 对象并装配到 Spring 容器中。在这个上下文中,它指示当前类是一个 Spring 配置类。
@SpringBootConfiguration //标明该类为配置类
//这个注解是 Spring Boot 自动配置的核心。当应用启动时,Spring Boot 会根据 classpath 中存在的库(JAR 文件)来自动配置 Bean。
@EnableAutoConfiguration //启动自动配置功能
//这个注解用于启用组件扫描,以便发现和管理带有特定注解的类(如 @Component、@Service、@Repository 和 @Controller 等)。如果没有指定扫描的包路径,那么默认会扫描包含 @SpringBootApplication 注解的类所在的包及其子包。
@ComponentScan( //启动包扫描
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
//通过使用 @SpringBootApplication 注解,开发者可以在单一的类中定义整个应用程序所需的配置,而不需要在 XML 或 Java 配置文件中进行大量的手动配置。这正是 Spring Boot 的核心理念之一:约定优于配置。
public @interface SpringBootApplication {
//根据class来排除特定的自动配置类,使其不能加入spring容器,传入参数value类型是class类型
@AliasFor(
annotation = EnableAutoConfiguration.class
)
//这个方法是 @EnableAutoConfiguration 注解的一个别名,用于指定要排除的自动配置类。
Class<?>[] exclude() default {};
//根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组
@AliasFor(
annotation = EnableAutoConfiguration.class
)
//这个方法也是 @EnableAutoConfiguration 注解的一个别名,用于指定要排除的自动配置类的名称。
String[] excludeName() default {};
//指定扫描包,参数是包名的字符串数组,用于激活@Component(元件)等注解类的初始化
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
//这个方法是 @ComponentScan 注解的一个别名,用于指定要扫描的基础包。
String[] scanBasePackages() default {};
//扫描特定的包,参数类是Class类型数组
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
//这个方法也是 @ComponentScan 注解的一个别名,用于指定要扫描的基础包中的类。
Class<?>[] scanBasePackageClasses() default {};
//bean名称生成器,用于命名被扫描到并注册成spring容器中的bean的bean名称
@AliasFor(
annotation = ComponentScan.class,
attribute = "nameGenerator"
)
//这个方法是 @ComponentScan 注解的一个别名,用于指定 Bean 名称生成器。
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* 注解的意思是proxyBeanMethods配置类是用来指定 @Bean 注解标注的方法是否使用代理,默认是 true 使用代理
* (proxyBeanMethods = true)[保证每个@Bean方法被调用多少次返回的组件都是新创建的]
*/
@AliasFor(
annotation = Configuration.class
)
//这个方法是 @Configuration 注解的一个别名,用于指定是否代理 bean 方法。默认为 true,表示会代理。
boolean proxyBeanMethods() default true;
}
@AliasFor
注解,该注解通常用于桥接到其他注解,该注解的属性中指定了所桥接的注解类,@SpringBootApplication
定义的属性在"三体"组合注解中已经定义过了,之所以使用@AliasFor
注解并重新在@SpringBootApplication
中进行定义,更多的是为了减少用户使用多注解带来的麻烦
Full全模式,Lite轻量级模式
boolean proxyBeanMethods() default true;
- Full(proxyBeanMethods = true):
proxyBeanMethods
参数设置为true时即为:Full全模式
。该模式下注入容器中的同一个组件无论被取出多少次都是同一个bean
实例,即单实例对象,在该模式下Spring Boot
每次启动都会判断检查容器中是否存在该组件。 - Lite(proxyBeanMethods = false)
proxyBeanMethods
参数设置为false时即为:Lite轻量级模式
。该模式下注入容器中的同一个组件无论被取出多少次都是不同的bean
实例,即多实例对象,在该模式下Spring Boot
每次启动会跳过检查容器中是否存在该组件。
什么时候用Full全模式,什么时候用Lite轻量级模式?
- 当在你的同一个
Configuration
配置类中,注入到容器中的bean
实例之间有依赖关系时,建议使用Full全模式
- 当在你的同一个
Configuration
配置类中,注入到容器中的bean
实例之间没有依赖关系时,建议使用Lite轻量级模式
,以提高Spring Boot
的启动速度和性能
1.2 @ComponentScan
@ComponentScan
注解用于启动组件扫描,以便发现和管理带有特定注解的类(如 @Component
、@Service
、@Repository
和 @Controller
等)。在这个注解中使用了两个 @Filter
,它们的作用是排除某些类或包从组件扫描中。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
-
1.
TypeExcludeFilter
:
这是一个 Spring Boot 内置的过滤器,它会排除所有以 .package-info 结尾的文件。这些文件通常包含一些包级别的 Javadoc 注释信息,而不是实际的类定义。因此,将它们排除在组件扫描之外可以提高扫描效率。 -
2.
AutoConfigurationExcludeFilter
:
这个过滤器是 Spring Boot 自动配置的一部分,它会排除那些已经被自动配置处理过的类。因为自动配置已经为这些类创建了相应的 Bean,所以没有必要再通过组件扫描来发现它们。
综上所述,这段代码的意思是:在进行组件扫描时,除了排除以 .package-info 结尾的文件外,还排除那些已经被自动配置处理过的类。这样做的目的是避免重复地创建相同的 Bean,从而优化应用程序的启动速度和运行性能。
1.3 EnableAutoConfiguration
@EnableAutoConfiguration
是 Spring Boot 中的一个核心注解,用于启用自动配置功能。让我们从源码的角度来解释这个注解的工作原理:
1.定义:
@EnableAutoConfiguration 注解是通过在 Java 类上使用来启用自动配置的。它的定义如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
@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 {};
}
-
组合注解:
@EnableAutoConfiguration
本身是一个复合注解,它包含两个嵌套注解:@AutoConfigurationPackage
:这个注解用于创建一个特殊的 Bean,该 Bean 可以确定主配置包的位置,并将所有扫描的基础包添加到同一个逻辑容器中。@Import(AutoConfigurationImportSelector.class)
:这个注解告诉 Spring 容器要导入一个名为AutoConfigurationImportSelector
的类。这个类是自动配置的核心实现,负责根据 classpath 中存在的库来决定哪些自动配置类应该被激活。
-
自动配置过程:
当你在一个类上使用@EnableAutoConfiguration
注解时,Spring 容器会读取该注解并执行以下步骤:a. 创建一个
AutoConfigurationImportSelector
实例。
b. 调用AutoConfigurationImportSelector.selectImports()
方法,该方法会遍历 classpath 中的META-INF/spring.factories
文件,并查找与org.springframework.boot.autoconfigure.EnableAutoConfiguration
关联的所有条目。
c. 对于每个找到的自动配置类,检查是否满足@Conditional
注解所指定的条件。如果满足条件,则将该自动配置类添加到待导入的列表中。
d. 将列表中的所有自动配置类注入到 Spring 容器中,这些类通常会定义一些额外的 Bean 来提供所需的功能。 -
自定义排除:
@EnableAutoConfiguration
注解允许通过exclude
和excludeName
属性来排除特定的自动配置类。这两个属性分别对应了类名和类的全限定名。
总结起来,@EnableAutoConfiguration
注解通过组合其他注解和利用 AutoConfigurationImportSelector
类实现了 Spring Boot 自动配置的核心功能。它会根据 classpath 中存在的库自动配置所需的 Bean,从而简化应用程序的配置工作。
1.4 逐行解释getAutoConfigurationEntry方法
这段代码是 AutoConfigurationImportSelector
类的 getAutoConfigurationEntry
方法的实现。这个方法负责处理 @EnableAutoConfiguration
注解并确定要导入的自动配置类列表。让我们逐行解释:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//这一行检查 @EnableAutoConfiguration 注解是否启用。如果注解被禁用,则直接返回一个空的 AutoConfigurationEntry 对象。
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//这一行从 annotationMetadata 中获取 @EnableAutoConfiguration 注解的所有属性,并将它们封装为一个 AnnotationAttributes 对象。
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//这一行从 META-INF/spring.factories 文件中获取所有与 org.springframework.boot.autoconfigure.EnableAutoConfiguration 关联的条目,这些条目表示了可能需要导入的自动配置类列表。
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//这一行移除 configurations 列表中的重复项。
configurations = this.removeDuplicates(configurations);
//这一行从 @EnableAutoConfiguration 注解的 exclude 和 excludeName 属性中获取排除的自动配置类列表。
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//这一行检查 exclusions 列表中的类是否存在于 configurations 列表中,如果不包含则抛出异常。
this.checkExcludedClasses(configurations, exclusions);
//这一行从 configurations 列表中移除所有出现在 exclusions 列表中的类。
configurations.removeAll(exclusions);
//这一行使用 getConfigurationClassFilter() 方法获取一个过滤器,该过滤器用于进一步筛选 configurations 列表中的类。
configurations = this.getConfigurationClassFilter().filter(configurations);
//这一行触发一个事件,通知其他监听者有关自动配置导入的信息。
this.fireAutoConfigurationImportEvents(configurations, exclusions);
//最后,返回一个新的 AutoConfigurationImportSelector.AutoConfigurationEntry 对象,其中包含了待导入的自动配置类列表和排除的自动配置类列表。
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}