文章目录
@SpringBootApplication
@SpringBootConfiguration
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
指定当前类是一个配置类@Configuration
,在启动Spring容器时就会用到该配置类,通过解析该配置类最终会往Spring容器中添加很多的BeanDefinition。
@EnableAutoConfiguration自动配置
SpringBoot的自动配置类主要的实现就是@EnableAutoConfiguration
,它的作用有两个:
- 将配置类的包扫描路径封装成一个bean对象存入Spring容器中
- 通过@Import+DeferredImportSelector实现解析spring.factories文件中指定的自动配置类,并把这些自动配置类都加载进Spring容器中,只不过这个过程是比较靠后执行的,因为DeferredImportSelector
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
@AutoConfigurationPackage
该注解的作用就是将配置类包扫描的路径封装成一个Bean,存入Spring容器中,以供第三方来使用。就比如Mybatis的自动配置类会拿这个bean,去进行mapper扫描,去扫描接口+@Mapper
而AutoConfigurationImportSelector.class
类它是DeferredImportSelector
接口的实现类,并重写了getImportGroup()
方法
public Class<? extends Group> getImportGroup() {
// 在DeferredImportSelector接口类型中,如果重写了该方法并且返回不为null,那么则不会调用上方的selectImports()方法
// 转而去调用这里返回对象中的selectImports()方法
return AutoConfigurationGroup.class;
}
这里就会去调用AutoConfigurationGroup
类的process()
方法和selectImports()
方法,去读取+过滤spring.factories文件中的自动配置类,并最终将selectImport()方法返回的自动配置类加载进Spring容器并解析
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 获取所有自动配置类,并赋值给autoConfigurationEntries和entries
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 每个自动配置类对应annotationMetadata,selectImports()方法会取出来用
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
@Override
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
// 给自动配置类排序
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
去spring.factories文件中取自动配置类详情
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取spring.factories中所有的AutoConfiguration
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重(也就是按类名去重)
configurations = removeDuplicates(configurations);
// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 排除
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤
// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition
// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了
// 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤
// spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的
configurations = getConfigurationClassFilter().filter(configurations);
// configurations表示合格的,exclusions表示被排除的,把它们记录在ConditionEvaluationReportAutoConfigurationImportListener中
fireAutoConfigurationImportEvents(configurations, exclusions);
// 最后返回的AutoConfiguration都是符合条件的
return new AutoConfigurationEntry(configurations, exclusions);
}
DeferredImportSelector接口
这是Spring的知识,这里做一个回忆复习
DeferredImportSelect接口是ImportSelect接口的子接口,他们之间的区别如下:
-
ImportSelect接口,解析配置类时在使用@Import注解,如果导入的类型是ImportSelect,所导入的配置类会直接进行解析导入
-
DeferredImportSelect接口,延迟解析,该接口所导入的配置类会在其他配置类解析完之后再进行解析
DeferredImportSelect接口支持分组,可以实现getImportGroup()方法以及定义Group对象,就相当于指定了DeferredImportSelect所导入进来的配置类所属的组。比如SpringBoot就把所有的自动配置类单独做成了分组AutoConfigurationGroup
@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
主要作用:Spring容器启动时使用配置类,而解析配置类的过程中就会用到这里的@ComponentScan
进行包扫描,生成当前配置类所在目录下的所有BeanDefinition。
而这里的excludeFilters
添加了两个排除过滤器,
-
从BeanFactory中找所有的
TypeExcludeFilter
类型的bean,调用该对象的match()
方法进行匹配,如果方法true则表示需要排除所以这其实就是一个扩展机制,如果我们想要添加新的过滤机制,就直接实现该接口并且添加进spring容器即可,可以采用容器初始化器在包扫描之前就将它添加进Spring容器。也就是自定义一个类实现
ApplicationContextInitializer
接口,这个自定义类还需要在spring.factories文件中定义。 -
排除掉加了@Configuration注解,并且在spring.factories文件中已经定义了的自动配置类。因为这些自动配置类在
@EnableAutoConfiguration
注解中就会去处理这些自动配置类,不需要这里包扫描进行处理。
条件注解
SpringBoot中的常用条件注解有:
- ConditionalOnBean:是否存在某个某类或某个名字的Bean
- ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
- ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
- ConditionalOnClass:是否存在某个类
- ConditionalOnMissingClass:是否缺失某个类
- ConditionalOnExpression:指定的表达式返回的是true还是false
- ConditionalOnJava:判断Java版本
- ConditionalOnWebApplication:当前应用是不是一个Web应用
- ConditionalOnNotWebApplication:当前应用不是一个Web应用
- ConditionalOnProperty:Environment中是否存在某个属性
SpringBootAOP自动配置
源码如下
@Configuration(proxyBeanMethods = false)
// 判断spring.aop.auto这个值是不是为true,如果没有配置则采用matchIfMissing指定的配置项
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
// 当前项目中必须要有Advice这个类才符合这个条件,默认情况下没有该类,需要我们导入spring-boot-starter-aop依赖
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
// 在Spring中如果想要使用AOP,那么就需要加上@EnableAspectJAutoProxy注解,其实就是导入一个BeanPostProcessor
// 在使用ProxyFactory创建代理对象时,就会判断proxyTargetClass这个属性,如果为false则使用JDK的动态代理
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
// 在Spring中如果想要使用AOP,那么就需要加上@EnableAspectJAutoProxy注解,其实就是导入一个BeanPostProcessor
// 在使用ProxyFactory创建代理对象时,就会判断proxyTargetClass这个属性,如果为true则使用cjlib的动态代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
// 通过下面这个判断条件可知SpringBoot默认情况下是强制使用的cglib动态代理,除非spring.aop.proxy-target-class=false
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
@Bean
static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
return (beanFactory) -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
};
}
}
}
我们需要导入下面的依赖,当前项目中才会存在Advice
这个类,这样才能使用AOP
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
补充知识
SpringBoot配置项优先级
配置SpringBoot的几个位置:
-
Program arguments 命名行参数:–server.port=8081
-
VM options JVM参数:-Dserver.port=8082
-
Environment variables 操作系统的环境变量:server.port=8083
-
application.properties文件 server.port=8084
- spring.config.import=/XXX/
- spring.config.additional-location=/XXX/
- optional:file:./config/*/
- optional:file:./config/
- optional:file:./
- optional:classpath:/config/
- optional:classpath:/
- 我们还可以在命名行参数/JVM参数/操作系统的环境变量中使用spring.config.import或spring.config.additional-location来指定properties配置文件路径。注意,这两个配置项必须要在这三个位置中的其中一个来进行指定,不能在properties文件中指定,因为此时properties文件还没有进行加载解析
- 用户指定的配置文件路径优先级最高,其次默认情况下file目录高于classpath目录,
- file目录就是项目的根路径或者是运行jar包时jar包所在的目录;
- classpath目录就是使用idea开发时,和java目录平级的resource目录下。java目录和平级的resource目录都是idea为我们更好的开发提供的,编译之后是没有resource目录的,它其中的文件都是直接在classpath目录下的
- 某个目录下的properties文件优先级高于yaml文件
- optional的作用是我这指定的目录如果不存在也不会报错,在指定的目录下没有找到对应的application.properties文件也不会报错