前言:
使用 Spring Boot 的都知道,我们只需要在启动类上加上一个 @SpringBootApplication 注解,就可以轻松的完成项目启动及各种 bean 的加载,也就是我们常说的自动装配,那你知道自动装配是怎么完成的吗? @SpringBootApplication 注解是自动装配不可或缺的一环,本文我们就分析一下 @SpringBootApplication 注解。
@SpringBootApplication 注解源码分析:
@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 {
@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 = ComponentScan.class,
attribute = "nameGenerator"
)
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
我们看到 @SpringBootApplication 注解上有又有7个注解,因此可以知道的是 @SpringBootApplication 注解是一个组合注解,我们分析一下这7个注解,如下:
- @Target({ElementType.TYPE}):限制注解使用的范围,比如使用在类、接口、枚举、方法、成员变量、局部变量、方法参数、注解、包等,ElementType.TYPE 表示可以修饰类、接口、注解或枚举类型。
- @Retention(RetentionPolicy.RUNTIME):用于指定被修饰的注解可以保留多长时间,Retention.SOURCE 表示只在源码中保留,在编译期间删除,Retention.CLASS 表示在编译期存在,在运行时不可获取,是默认值,RetentionPolicy.RUNTIME 表示在运行时有效,可以使用反射获取注解信息。
- @Documented:表示被修饰的注解可以被 javadoc 工具提取成文档,定义注解类时使用 @Document 进行修饰,则使用该注解修饰的程序的 API 文档中将会包含该注解说明。
- @Inherited:表示注解具有继承性,如果某个注解使用 @Inherited 进行修饰,则该类使用该注解时,其子类将自动被修饰。
- @SpringBootConfiguration:内部结构是 @Configuration 注解,@Configuration我们就非常熟悉了,标明当前类是一个配置类,项目启动时该类会被加载到 spring 容器中。
- @EnableAutoConfiguration:开启自动配置功能,实现自动装配的核心注解,内部包含两个注解,@AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)。
- @ComponentScan:默认扫描 @ComponentScan 注解所在包及子包中标注了 @Component 注解的类以及衍生注解标注的类(@Repository、@Service、@Controller)。
这7个注解中,其中前面4个是 jdk 的元注解,后面3个注解是需要重点关注的:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。
@SpringBootConfiguration 注解源码分析:
从源码中可以看到 @SpringBootConfiguration 就是一个特殊的 @Configuration 注解,在 Spring Boot 应用程序中,它表示该类是一个配置类,并且可以被自动加载到 IOC 容器中。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
属性分析:
- proxyBeanMethods:表示被 @Configuration 注解修饰的类是否会被代理,true 表示会被代理且是单例模式,false 表示不会被代理且不是单例模式,默认值是 true。
@Configuration 注解源码分析:
从源码中可以看到 @Configuration 又是一个特殊的 @Component 注解,被 @Configuration 注解标注的类,能够自动注册到IOC容器并进行实例化。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
属性分析:
- value:Bean 在 Spring IOC 容器中的 id。
- proxyBeanMethods:表示被 @Configuration 注解修饰的类是否会被代理,true 表示会被代理且是单例模式,false 表示不会被代理且不是单例模式,默认值是 true。
@EnableAutoConfiguration 注解源码分析:
@EnableAutoConfiguration 注解开启自动配置功能,是 Spring Boot 最核心的一个特性,也是约定大于配置设计思想的主要体现,从源码中可以看到 @EnableAutoConfiguration 内部包含两个注解 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)组成。
@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 {};
}
属性分析:
- exclude:排除指定的自动配置类,让它们不会被自动装配到 Spring IOC 容器中。
- proxyBeanMethods:excludeName:排除指定自动配置类名,让它们不会被自动装配到 Spring IOC 容器中。
@AutoConfigurationPackage 注解源码分析:
通过源码可以看到 @AutoConfigurationPackage 注解又使用了 @Import({Registrar.class}) 注解,其他没有任何东西,也就是发挥左右的主要是 Registrar 类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@Registrar 注解源码分析:
从源码看出 Registrar 是 AutoConfigurationPackages 的内部类,Registrar 实现了 ImportBeanDefinitionRegistrar 接口 的 register 方法,register 方法有一个参数是 BeanDefinitionRegistry,BeanDefinitionRegistry 是一个接口,实际上用的是 DefaultListableBeanFactory 类,DefaultListableBeanFactory 类有一个 beanDefinitionMap 属性,beanDefinitionMap key 为 bean 名称,value 为 bean 对象本身,这里其实就是走到类 Spring IOC 容器的核心部分,也就是为什么 Spring Boot 可以自动装配的原因,其实就是利用注解然后调用到 Spring 的 IOC 相关的接口实现自动装配。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
@Import(AutoConfigurationImportSelector.class) 注解源码分析:
AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口,DeferredImportSelector 接口又实现了 ImportSelector 类selectImports 是 AutoConfigurationImportSelector 类的核心方法,selectImports 方法返回的是需要自动装配类的全限类名,也就是获取 META-INF/spring.factories 文件的全限定名,完成这些类自动装配,这里其实还用到了 Spring 的 SPI 机制,AutoConfigurationImportSelector 配合SpringFactoriesLoader 完成 META-INF/spring.factories 文件中全限定名对应的类的自动装配。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
....................
}
public interface DeferredImportSelector extends ImportSelector {
....................
}
//selectImports 方法是 AutoConfigurationImportSelector 类的核心方法,
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//获取自动装配的类
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获取需要自动专装配的类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//这里调用了 SpringFactoriesLoader 类的 loadFactoryNames 方法 这里起始设计到了SPI
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
//这里的提示已经说明了获取的是 META-INF/spring.factories 下的类
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;
}
什么是 SPI ?
Java SPI 全称 Service Provider Interface,是一种服务发现机制,它允许在运行时动态地加载实现特定接口的类,而不需要在代码中显式地指定该类,从而实现解耦和灵活性,ava SPI 是基于 Java 类加载机制和反射机制实现的,当使用 ServiceLoader.load() 方法时会检查 META-INF/services 目录下是否有类的全限定名命名的文件,如果有则读取文件中的接口实现类全限定名,并通过 Class.forName() 方法加载对应的类,Spring 对 Java SPI 技术做了扩展。
@ComponentScan 注解源码分析:
@ComponentScan 注解主要作用就是指定扫描路径,Spring 会把 @ComponentScan 注解扫描到的类且带有指定注解(比如:@Controller、@Service、@Component、@Repository)的类注册到 Spring IOC 容器中。
@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 {};
}
}
属性分析:
- basePackages:扫描哪些包下的类。
- value:和 basePackages 一样的作用,扫描哪些包下的类。
- useDefaultFilters:默认使用过滤策略,一般和 includeFilters、excludeFilters 一起使用。
- includeFilters:指定扫描哪些包下的类,和 useDefaultFilters 一起使用。
- excludeFilters:指定不扫描哪些包下的类,和 useDefaultFilters 一起使用。
- lazyInit:是否开启懒加载,由于是包扫描一次性的,不能单独配置哪个组件是否懒加载,因此提供该属性用于声明是否开启懒加载。
Filter 自定义过滤规则
Filter 自定义过滤规则,通过 FilterType 来设置自定义过滤规则的类型,自定义过滤规则枚举值如下:
- FilterType.ANNOTATION:按照注解过滤。
- FilterType.ASSIGNABLE_TYPE:按照给定的类型过滤。
- FilterType.ASPECTJ:按照 ASPECTJ 表达式过滤。
- FilterType.REGEX:按照正则表达式过滤。
总结:本文简单分析了 @SpringBootApplication 注解的源码,有助于我们更深入理解 Spring Boot 的自动装配及约定大于配置的理念,同时对我们开发中合理使用注解也有帮助,希望可以帮助到有需要的小伙伴。
欢迎提出建议及对错误的地方指出纠正。