【Spring】SpringBoot自动注入原理分析,@SpringBootApplication、@EnableAutoConfiguration详解
文章目录
- SpringBoot 原理分析
- 源码阅读
- @SpringBootApplication
- 1. 元注解
- 2. @SpringBootConfiguration
- 3. @EnableAutoConfiguration
- 4. @ComponentScan
- @EnableAutoConfiguration 详解
- @Import ({AutoConfigurationImportSelector. class})
- @AutoConfigurationPacket
- 总结
- 总结
SpringBoot 原理分析
源码阅读
SpringBoot
是如何帮助我们做的呢?一切来自起源 SpringBoot
的启动类开始
@SpringBootApplication
标注的类就是SpringBoot
项目的启动类
@SpringBootApplication
public class SpringIocApplication {public static void main(String[] args) {// 1. 获取Spring上下文对象(启动应用)ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);// 2. 从容器中获取Bean实例BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);// 3. 使用BeanbeanLifeComponent.use();}
}
这个类和普通类唯一的区别就是 @SpringBootApplication
注解,这个注解也是 SpringBoot
实现自动配置的核心
@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 {// ...代码省略
}
@SpringBootApplication
这是一个组合注解,里面包含了
- 元注解
@SpringBootConfiguration
EnableAutoConfiguration
(开启自动配置)ComponentScan
(包扫描)
1. 元注解
JDK
中提供了 4 个标准的用来对注解类型进行注解的注解类,我们称之为 meta-annotation
(元注解),他们分别是:
@Target
:描述注解的使用范围(即被修饰的注解可以用在什么地方)Retention
:描述注解保留的时间范围@Documented
:描述在使用javadoc
工具为类生成帮助文档时是否要保留其注解信息Inherited
:使被它修饰的注解具有继承性(如果每个类使用了被@Inherited
修饰的注解,则其子类将自动具有该注解)
2. @SpringBootConfiguration
里面就是 @Configuration
,标注当前类为配置类,其实只是做了一层封装,改了个名字而已
@Indexed
注解,是用来加速应用启动的,不用关心
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true;
}
3. @EnableAutoConfiguration
Spring
自动配置的核心注解,下面详细讲解
4. @ComponentScan
- 可以通过
basePackageClasses
或basePackages
来定义需要扫描的特定包 - 如果没有定义特定的包,将从声明该注解的类的包开始扫描,这也是为什么
SpringBoot
项目声明的注解类必须要在启动类的目录下 excludeFilters
自定义过滤器,通常用于排除一些类,注解等
@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 {};
}
这个注解包含两部分:
@Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage
@Import ({AutoConfigurationImportSelector. class})
使用 @Import
注解,导入了实现 ImportSelector
接口的实现类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { // 获取自动配置的配置类信息AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
selectImport()
方法底层调用getAutoConfigurationEntry()
方法,获取可自动配置的配置类信息集合
点进去:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.<String>removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.getConfigurationClassFilter().filter(configurations); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
}
getAutoConfigurationEntry()
方法通过调用 getCandidateConfigurations(annotationMetadata, attributes)
方法获取在配置文件中配置的所有自动配置类的集合
点进去:
//获取所有基于
// META-INF/spring/aot.factories文件
// META-INF/spring.factories
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, getBeanClassLoader()); List<String> configurations = importCandidates.getCandidates(); Assert.state(!CollectionUtils.isEmpty(configurations), "No auto configuration classes found in " + "META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you " + "are using a custom packaging, make sure that file is correct."); return configurations;
}
getCandidateConfigurations
方法的功能:- 获取所有基于
META-INF/spring/aot.factories
文件,META-INF/spring.factories
文件中配置类的集合
- 获取所有基于
在引入的起步依赖中,通常都有包含以上两个文件
- 这里面包含了很多第三方依赖的配置文件(连续按两下
shift
可以查看对应的源码)-
在加载自动配置类的时候,并不是将所有的配置全部加载进来,而是通过
@Conditional
等注解的判断进行动态加载。
@Conditional
是Spring
底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效 -
META-INF/spring/aot.factories
文件是Spring
内部提供的一个约定俗成的加载方式,只需要在模块的META-INF/spring/aot.factories
文件中配置即可,Spring
就会把响应的实现类注入到Spring
容器中
-
注:会加载所有 jar
包下的 classpath
路径下的 META-INF/spring/aot.factories
文件,这样文件不止一个
- 比如
redis
的配置:RedisAutoConfiguration
- 可以看到,配置文件中使用
@Bean
声明了一些对象,Spring
就会自动调用配置类中使用@Bean
标识的方法,并把对象注册到Spring IoC
容器中
- 在加载自动配置类的时候,并不是将所有的配置全部加载进来,而是通过
@Conditional
等注解的判断进行动态加载@Conditional
是Spring
底层注解,意思就是会根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里面的配置才会生效
@AutoConfigurationPacket
源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage { String[] basePackages() default {}; Class<?>[] basePackageClasses() default {};
}
这个注解主要是导入了一个配置文件 AutoConfigurationPackages.Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { Registrar() { } public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])); } public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImports(metadata)); }
}
- 这个类实现了
ImportBeanDefinitionRegistrar
类,就可以被注解@Import
注解导入到Spring
容器里 (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])
:当前启动类所在的包名
所以:@AutoConfigurationPacket
就是将启动类所在的包下面的所有组件都扫描注册到 Spring
容器中
总结
SpringBoot
自动配置原理的大概流程如下:
当 SpringBoot
程序启动时,会加载配置文件当中所定义的配置类,通过 @Import
注解将这些配置类全部加载到 Spring
的 IoC
容器中,交给 IoC
容器管理
总结
Bean
的作用域共分为 6 中:singleton
、prototype
、request
、session
、application
和websocket
Bean
的生命周期共分为 5 大部分:实例化、属性复制、初始化、使用和销毁SpringBoot
的自动配置原理源码口是:@SpringBootApplication
注解,这个注解封装了 3 个注解@SpringBootConfiguration
标志当前类为配置类@ComponentScan
进行包扫描(默认扫描的是启动类所在的当前包及其子包)@EnableAutoConfiguration
@Import
:读取当前项目下所有依赖 jar 包中META-INF/spring.facctories
,META-INF/spring/aot.factories
两个文件里面定义的配置类(配置类中定义了@Bean
注解标识的方法)@AutoConfigurationPacket
:把启动类所在包下面所有的组件都注入到Spring
容器中