@SpringBootApplication注解学习
1、说明
springboot框架的启动类
2、组成
@SpringBootConfiguration 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan // 包扫描器 <context:component-scan base-package="com.xxx.xxx"/>
2.1、@SpringBootConfiguration注解
@Configuration //配置类
public @interface SpringBootConfiguration {
}
说明:
@Configuration 该注解的作用是标识当前类为配置类
总结:
标识启动类为配置类
2.2、@EnableAutoConfiguration注解
@AutoConfigurationPackage //自动配置包 : 会把@springbootApplication注解标注的类所在包名拿到,并且对该包及其子包进行扫描,将组件添加到容器中
@Import(AutoConfigurationImportSelector.class) //可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中
public @interface EnableAutoConfiguration {}
2.2.1、@AutoConfigurationPackage注解
//spring框架的底层注解,它的作用就是给容器中导入某个组件类,
//例如@Import(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类导入到容器中
@Import(AutoConfigurationPackages.Registrar.class) // 默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中
public @interface AutoConfigurationPackage {
}
(1)、AutoConfigurationPackages.Registrar类说明
// 获取的是项目主程序启动类所在的目录
//metadata:注解标注的元数据信息
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
register(registry, new PackageImport(metadata).getPackageName());
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// 如果已经存在该 BEAN ,则修改其包(package)属性
// BEAN 就是 AutoConfigurationPackages,用于存储自动配置包以供稍后引用
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
// 将构造函数的第一个参数设置为包名列表
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
// 如果不存在该 BEAN ,则创建一个 Bean ,并进行注册
} else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
// 将beanClass设置为BasePackages
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
// 将构造函数的第一个参数设置为包名列表,也就是BasePackages的构造函数
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册beanDefinition
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
(2)、总结:
自动配置包 : 将启动类包下的类和子包进行扫描,并放入容器
2.2.2、@Import(AutoConfigurationImportSelector.class)
// 这个方法告诉springboot都需要导入那些组件
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//判断 enableautoconfiguration注解有没有开启,默认开启(是否进行自动装配)
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件
//作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。
// SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类
// 自动配置的类全名.条件=值
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
//1.读取spring-boot-autoconfigure.jar包中spring-autoconfigure-metadata.properties的信息生成urls枚举对象
// 获得 PATH 对应的 URL 们
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
// 遍历 URL 数组,读取到 properties 中
Properties properties = new Properties();
//2.解析urls枚举对象中的信息封装成properties对象并加载
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
// 将 properties 转换成 PropertiesAutoConfigurationMetadata 对象
//根据封装好的properties对象生成AutoConfigurationMetadata对象返回
return loadMetadata(properties);
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
/**
* 获得 AutoConfigurationEntry 对象
*
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param autoConfigurationMetadata the auto-configuration metadata
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
// 1. 判断是否开启注解。如未开启,返回空串
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 2. 获得注解的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3. getCandidateConfigurations()用来获取默认支持的自动配置类名列表
// spring Boot在启动的时候,使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,
// 找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性定义的工厂类名称,
// 将这些值作为自动配置类导入到容器中,自动配置类就生效了
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 3.1 //去除重复的配置类,若我们自己写的starter 可能存在重复的
configurations = removeDuplicates(configurations);
// 4. 如果项目中某些自动配置类,我们不希望其自动配置,我们可以通过EnableAutoConfiguration的exclude或excludeName属性进行配置,
// 或者也可以在配置文件里通过配置项“spring.autoconfigure.exclude”进行配置。
//找到不希望自动配置的配置类(根据EnableAutoConfiguration注解的一个exclusions属性)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 4.1 校验排除类(exclusions指定的类必须是自动配置类,否则抛出异常)
checkExcludedClasses(configurations, exclusions);
// 4.2 从 configurations 中,移除所有不希望自动配置的配置类
configurations.removeAll(exclusions);
// 5. 对所有候选的自动配置类进行筛选,根据项目pom.xml文件中加入的依赖文件筛选出最终符合当前项目运行环境对应的自动配置类
//@ConditionalOnClass : 某个class位于类路径上,才会实例化这个Bean。
//@ConditionalOnMissingClass : classpath中不存在该类时起效
//@ConditionalOnBean : DI容器中存在该类型Bean时起效
//@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
//@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
//@ConditionalOnExpression : SpEL表达式结果为true时
//@ConditionalOnProperty : 参数设置或者值一致时起效
//@ConditionalOnResource : 指定的文件存在时起效
//@ConditionalOnJndi : 指定的JNDI存在时起效
//@ConditionalOnJava : 指定的Java版本存在时起效
//@ConditionalOnWebApplication : Web应用环境下起效
//@ConditionalOnNotWebApplication : 非Web应用环境下起效
//总结一下判断是否要加载某个类的两种方式:
//根据spring-autoconfigure-metadata.properties进行判断。
//要判断@Conditional是否满足
// 如@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })表示需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类才能完成自动注册。
configurations = filter(configurations, autoConfigurationMetadata);
// 6. 将自动配置导入事件通知监听器
//当AutoConfigurationImportSelector过滤完成后会自动加载类路径下Jar包中META-INF/spring.factories文件中 AutoConfigurationImportListener的实现类,
// 并触发fireAutoConfigurationImportEvents事件。
fireAutoConfigurationImportEvents(configurations, exclusions);
// 7. 创建 AutoConfigurationEntry 对象
return new AutoConfigurationEntry(configurations, exclusions);
}
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
// if (true) {
// return configurations;
// }
// 声明需要用到的变量
long startTime = System.nanoTime(); // 记录开始时间,用于下面统计消耗的时间
String[] candidates = StringUtils.toStringArray(configurations); // 配置类的数组
boolean[] skip = new boolean[candidates.length]; // 每个配置类是否需要忽略的数组,通过下标互相索引
boolean skipped = false; // 是否有需要忽略的
// 遍历 AutoConfigurationImportFilter 数组,逐个匹配
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 设置 AutoConfigurationImportFilter 的属性们
invokeAwareMethods(filter);
// 执行批量匹配,并返回匹配结果
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
// 遍历匹配结果,判断哪些需要忽略
for (int i = 0; i < match.length; i++) {
if (!match[i]) { // 如果有不匹配的
skip[i] = true;
candidates[i] = null; // 标记为空,循环的下一次,就无需匹配它了。
skipped = true; // 标记存在不匹配的
}
}
}
// 如果没有需要忽略的,直接返回 configurations 即可
if (!skipped) {
return configurations;
}
// 如果存在需要忽略的,构建新的数组,排除掉忽略的
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
// 打印,消耗的时间,已经排除的数量
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
// 返回
return new ArrayList<>(result);
}
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 加载指定类型 AutoConfigurationImportListener 对应的,在 `META-INF/spring.factories` 里的类名的数组。
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
// 创建 AutoConfigurationImportEvent 事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
// 遍历 AutoConfigurationImportListener 监听器们,逐个通知
for (AutoConfigurationImportListener listener : listeners) {
// 设置 AutoConfigurationImportListener 的属性
invokeAwareMethods(listener);
// 给AutoConfigurationImportListener监听器发送事件
listener.onAutoConfigurationImportEvent(event);
}
}
}
2.3、@ComponentScan注解
@ComponentScan(excludeFilters = { // 包扫描器 <context:component-scan base-package="com.xxx.xxx"/>
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
注释:
@Import 注解的作用是将指定的类加载到ioc容器中