共计 5041 个字符,预计需要花费 13 分钟才能阅读完成。
1. SpringBoot 主动拆卸原理
接触过 SpringBoot 的同学必定都晓得在启动类上有一个 @SpringBootApplication 注解,他就是主动拆卸的神秘所在。
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Andy Wilkinson
* @since 1.2.0
*/
@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 注解,先看它的正文,粗心是:
示意一个申明了一个或多个由 @Bean 注解的办法的配置类,并触发了主动配置和组件扫描。
它是一个等价于 @Configuration、@EnableAutoConfiguration 和@ComponentScan的更不便的注解。
能够看到,它由多个注解组合而成,最次要的就是三个注解:
(1) @SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {// 省略代码}
@SpringBootConfiguration其实就相当于@Configuration,就不做过多解释了。
(2) @EnableAutoConfiguration
这个注解是主动拆卸的外围注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {// 省略代码}
它由 @AutoConfigurationPackage 和@Import(AutoConfigurationImportSelector.class)组成:
1) @AutoConfigurationPackage
/**
* Registers packages with {@link AutoConfigurationPackages}. When no {@link #basePackages
* base packages} or {@link #basePackageClasses base package classes} are specified, the
* package of the annotated class is registered.
*
* @author Phillip Webb
* @since 1.3.0
* @see AutoConfigurationPackages
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {// 省略代码}
能够看下它的正文,粗心是:
应用 @AutoConfigurationPackage 注解来注册包。
当没有指定根底包或者根底包类时,被它所注解的类就会被注册。
它应用了 @Import 注解导入了 AutoConfigurationPackages.Registrar 这个外部类,它的作用是把扫描门路注册到容器的全局变量中,这样就能够提供给一些 JPA 框架用来查问到扫描门路。
2) @Import(AutoConfigurationImportSelector.class)
这里应用 @Import 导入了 AutoConfigurationImportSelector 类,这个才是最外围的注解。
在 AutoConfigurationImportSelector 中有一个 selectImports 办法,它会返回 所有须要加载到容器中的类的全门路,SpringBoot 在获取到这些类的全门路的时候,会应用反射的形式将它们注入到容器中。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
那 selectImports 是怎么做到返回这些类的全门路的呢?
重点在于 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);
}
能够看到在 getAutoConfigurationEntry 办法中,最重要的就是应用 getCandidateConfigurations 办法获取所有候选的配置类:
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
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;
}
能够看到,原来是应用了 Spring 的 SPI 机制来获取要加载的类的全门路的!
持续跟代码能够看到,在 SpringFactorieLoader 类中,通过类加载器去加载 META-INF/spring.factories 文件中的配置,轻易点开一个 SpringBoot 的 jar,比方 spring-boot-autoconfigure,能够看到果然有一个 META-INF/spring.factories 文件。
这个文件中配置了一系列 key-value 的值,这里咱们只关注 EnableAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
// 省略配置
看到这里应该大略就明确了 SpringBoot 主动拆卸的原理了,其实就是通过读取各个 jar 包中的 META-INF/spring.factories 中的配置项,来获取类的全门路,而后通过 @Configuration 和 @Bean 注入到容器中。
(3) @ComponentScan
这个注解应该很相熟了,是用来设置扫描门路的,默认状况下,它会将以后包以及其子包中的 Bean 注入到容器中。