@SpringBootApplication注解
对这个注解详细大家一定非常熟悉了。再来好好看看这个注解。
我们点进该注解,发现它由多个注解构成。
这种注解 注解注解的方式实在看着让人头疼。
我们主要看这两个SpringBoot的注解,也就是 @SpringBootConfiguration和@EnableAutoConfiguration
我们点进 @SpringBootConfiguration注解发现他里面没有太多东西,只是对 @Configuration注解进行了一个封装。
简而言之,它就是一个 @Configuration 注解而已。
那只剩下@EnableAutoConfiguration注解可以做文章了,该注解水特别深
点进该注解后,我们看到有 @AutoConfigurationPackage和 @Import两个注解
发现该类实现了许多接口,我们把目光放到 DeferredImportSelector接口上,点进这个接口,发现它继承了ImportSelector接口。再往里点,发现ImportSelector接口 它有一个selectImports方法,到头了。
我们回到AutoConfigurationImportSelector类,看看selectImports这个方法被如何重写了。
首先 if判断会确认系统是否禁用了自动配置的功能。如果禁用了就会 return一个常量,该常量是一个空数组。也就无法注入bean了。
再往下能看到调用了一个 getAutoConfigurationEntry方法,我们看看这个方法干了什么。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 判断是否允许自动装配 若不允许则直接返回空集 默认返回true
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//getAttributes方法获得 @EnableAutoConfiguration注解中的属性exclude、excludeName等。
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// ==又一关键方法== 该方法用于得到spring.factories文件EnableAutoConfiguration相关的自动配置类
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);
// 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
return new AutoConfigurationEntry(configurations, exclusions);
}
AutoConfigurationEntry方法主要用来获取符合条件的自动配置类,和避免加载不需要的自动配置类(会造成内存的浪费)。
我们再来看看用于得到所有自动配置类的getCandidateConfigurations方法。
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;
}
是不是又看到一个十分熟悉的方法loadFactoryNames(); ,我们把镜头交到loadFactoryNames,再回顾一遍 SpringBoot是如何从 META-INF/spring.factories中加载指定 key的 value的。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 获取检索类的全限定名
String factoryTypeName = factoryType.getName();
// 进行spring.factories文件集的加载、检索操作,并构以空集为兜底返回数据
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
debug,看看要从META-INF/spring.factories中加载的类的key,如下图所示:org.springframework.boot.autoconfigure.EnableAutoConfiguration
回到selectImports()方法,debug,跳过List configurations = getCandidateConfigurations(annotationMetadata,attributes);看一下configurations
竟然有一百多个,那这些类都在哪里呢?看spring-boot-autoconfigure(当然在SpringBoot的工程中,也不止这一个依赖包中存在该配置文件)工程下的META-INF/spring.factories,我们能看到org.springframework.boot.autoconfigure.EnableAutoConfiguration定义了一大堆。
继续看
Set exclusions = getExclusions(annotationMetadata, attributes);方法,该方法是排除主类上
@SpringBootApplication注解上排除的自动装配的类。比如我们在该注解上排除我们自定义starter的自动装配的类,
@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})
(当然也可以用excludeName进行排除),那么在后面
configurations.removeAll(exclusions);
方法中将会删除我们的com.demo.starter.config.DemoConfig.class。
java@ConditionalOnBean:容器中有指定的Bean
@ConditionalOnClass:当类路径下有指定的类
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下
@ConditionalOnMissingClass:当类路径下没有指定的类
@ConditionalOnNotWebApplication:当前项目不是Web项目
@ConditionalOnProperty:配置文件中指定的属性是否有指定的值
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件下
上图这种没有满足@ConditionXXX注解的自动配置类,就不会被注入到IoC容器中
而这种满足了@ConditionXXX的自动配置类就会被导入到IoC容器中了
除了这些条件注解,我们还有一些注解可以讲
找到EnableConfigurationProperties注解,点进它这个类。
点进来后我们能看到一个前缀(spring.mvc),和非常多的属性。
不难猜到这些属性就是对应的我们能够在application.yaml配置文件中自定义配置的一些配置。
我们来看一下ConfigurationProperties这个注解
这个注解会将我们配置文件中的配置项和它prefix参数中的前缀进行匹配。
如果能够匹配到的话,就会将我们配置文件的配置项读到其前缀相匹配的类中
而EnableConfigurationProperties注解又将该类注入到了我们的IoC容器中,那我们启动SpringBoot主程序时也就自然而然的实现了自定义配置的操作。