SpringBoot自动化配置的注解开关原理
我们以一个最简单的例子来完成这个需求:定义一个注解EnableContentService,使用了这个注解的程序会自动注入ContentService这个bean。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ContentConfiguration.class)
public @interface EnableContentService {}
public interface ContentService {
void doSomething();
}
public class SimpleContentService implements ContentService {
@Override
public void doSomething() {
System.out.println("do some simple things");
}
}
然后在应用程序的入口加上@EnableContentService注解。
这样的话,ContentService就被注入进来了。 SpringBoot也就是用这个完成的。只不过它用了更加高级点的ImportSelector。
ImportSelector的使用
用了ImportSelector之后,我们可以在Annotation上添加一些属性,然后根据属性的不同加载不同的bean。
我们在@EnableContentService注解添加属性policy,同时Import一个Selector。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ContentImportSelector.class)
public @interface EnableContentService {
String policy() default "simple";
}
这个ContentImportSelector根据EnableContentService注解里的policy加载不同的bean。
public class ContentImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annotationType = EnableContentService.class;
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(
annotationType.getName(), false));
String policy = attributes.getString("policy");
if ("core".equals(policy)) {
return new String[] { CoreContentConfiguration.class.getName() };
} else {
return new String[] { SimpleContentConfiguration.class.getName() };
}
}
}
CoreContentService和CoreContentConfiguration如下:
public class CoreContentService implements ContentService {
@Override
public void doSomething() {
System.out.println("do some import things");
}
}
public class CoreContentConfiguration {
@Bean
public ContentService contentService() {
return new CoreContentService();
}
}
这样的话,如果在@EnableContentService注解的policy中使用core的话,应用程序会自动加载CoreContentService,否则会加载SimpleContentService。
ImportSelector在SpringBoot中的使用
SpringBoot里的ImportSelector是通过SpringBoot提供的@EnableAutoConfiguration这个注解里完成的。
这个@EnableAutoConfiguration注解可以显式地调用,否则它会在@SpringBootApplication注解中隐式地被调用。
@EnableAutoConfiguration注解中使用了EnableAutoConfigurationImportSelector作为ImportSelector。下面这段代码就是EnableAutoConfigurationImportSelector中进行选择的具体代码:
@Override
public String[] selectImports(AnnotationMetadata metadata) {
try {
AnnotationAttributes attributes = getAttributes(metadata);
List<String> configurations = getCandidateConfigurations(metadata,
attributes);
configurations = removeDuplicates(configurations); // 删除重复的配置
Set<String> exclusions = getExclusions(metadata, attributes); // 去掉需要exclude的配置
configurations.removeAll(exclusions);
configurations = sort(configurations); // 排序
recordWithConditionEvaluationReport(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
其中getCandidateConfigurations方法将获取配置类:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
return SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
}
SpringFactoriesLoader.loadFactoryNames方法会根据FACTORIES_RESOURCE_LOCATION这个静态变量从所有的jar包中读取META-INF/spring.factories文件信息:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName); // 只会过滤出key为factoryClassNames的值
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
getCandidateConfigurations方法中的getSpringFactoriesLoaderFactoryClass方法返回的是EnableAutoConfiguration.class,所以会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。
下面这段配置代码就是autoconfigure这个jar包里的spring.factories文件的一部分内容(有个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration,所以会得到这些AutoConfiguration):
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
当然了,这些AutoConfiguration不是所有都会加载的,会根据AutoConfiguration上的@ConditionalOnClass等条件判断是否加载。
上面这个例子说的读取properties文件的时候只会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。
SpringBoot内部还有一些其他的key用于过滤得到需要加载的类:
org.springframework.test.context.TestExecutionListener
org.springframework.beans.BeanInfoFactory
org.springframework.context.ApplicationContextInitializer
org.springframework.context.ApplicationListener
org.springframework.boot.SpringApplicationRunListener
org.springframework.boot.env.EnvironmentPostProcessor
org.springframework.boot.env.PropertySourceLoader
SpringBoot自动化配置的注解开关原理的更多相关文章
- SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理
springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...
- SpringBoot自动化配置之二:自动配置(AutoConfigure)原理、EnableAutoConfiguration、condition
自动配置绝对算得上是Spring Boot的最大亮点,完美的展示了CoC约定优于配置: Spring Boot能自动配置Spring各种子项目(Spring MVC, Spring Security, ...
- 让SpringBoot自动化配置不再神秘
本文若有任何纰漏.错误,还请不吝指出! 注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory.关于BeanFactory, ...
- SpringBoot自动化配置之四:@Conditional注解详解
前言 之前在分析spring boot 源码时导出可见@ConditionalOnBean 之类的注解,那么它到底是如何使用的以及其工作流程如何,我们这里就围绕以下几点来分析: @Conditiona ...
- SpringBoot自动化配置之四:SpringBoot 之Starter(自动配置)、Command-line runners
Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳这里 Starter ...
- SpringBoot自动化配置之三:深入SpringBoot:自定义EnableAutoConfiguration
前言 上面几篇文章介绍了SpringFramework的一些原理,这里开始介绍一下SpringBoot,并通过自定义一些功能来介绍SpringBoot的原理.SpringBoot在SpringFram ...
- springboot笔记-1.自动化配置的关键
最近发现看过的东西容易忘,但是写一遍之后印象倒是会深刻的多. 总所周知springboot极大的简化了java开发繁琐性,而其最大的优势应该就是自动化配置了.比如要使用redis,我们直接引入相关的包 ...
- springBoot 自动配置原理
在之前文章中说过,springBoot会根据jar包去添加许多的自动配置,本文就来说说为什么会自动配置,自动配置的原理时什么? springBoot在运行SpringApplication对象实例化时 ...
- SpringBoot自动配置原理学习
介绍 构建Springboot项目时我们会创建一个启动类 @SpringBootApplication public class DemoApplication { public static voi ...
随机推荐
- c++日志输出库 spdlog 简介(3)多线程控制台输出日志
spdlog源码分析:https://www.cnblogs.com/eskylin/p/6483199.html spdlog的异步模式使得spdLog可以支持多线程,于是写了一个多线程的小例子: ...
- ASP.NET控制HTTP缓存
请求 响应 If-Modified-Since Last-Modified If-None-Match ETag 至于Expires和Cache-Control 附上几张高手的图 Etag 在使用 ...
- 蚂蚁男孩.队列组件(Framework.Mayiboy.Queue)
它能做什么 主要是用来方便使用队列而诞生,该组件封装了Queue和Redis中的队列,能够通过简单配置就可以高效快速使用起来. 使用说明 一. 下载源码,自己手动编译,手动引用必要的程序集.(需 ...
- MessageBox实现自动延时关闭
1,首先新建一个winform窗体:MessageForm,设置StartPosition属性为Manual,Location属性值-500, -500 主要是为了让MessageForm窗体不显示在 ...
- C#实现墨卡托投影坐标系经纬度与米制单位之间的互转
using System; using GeoJSON.Net.Geometry; namespace GISWebService.Common { /// <summary> /// 墨 ...
- 批处理系列(13) -从视频导出高质量GIF图片
需要ffmpeg,配置ffmpeg到环境变量. 保存代码到HQGIF.bat,与视频同目录,管理员权限运行CMDcd到此目标目录: HQGIF.bat input_video_name.mp4 out ...
- PaaS服务之路漫谈(三)
此文已由作者尧飘海授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Monolithic架构在产品访问量很大的情况下,有可能常会导致整个产品迭代或升级过程不能按预期进行,或者上 ...
- 全球第一开源云ERP Odoo操作手册 模块安装和界面汉化指南
之前介绍了odoo的安装和初始设置以及数据库,下面来介绍odoo的模块安装和界面汉化. 1.4 模块安装和界面汉化 Odoo 的功能包含在不同的模块中, 刚创建的数据库除 Odoo 的核心模块外, 其 ...
- Mysql-基础+安装指南
安装指南: https://www.cnblogs.com/majj/p/9160383.html 小马哥 下载完后初始化操作数据库: 1. 将文件放在 : G:\软件\mysql-8.0.15-w ...
- 纯文本-FileOutputStream的解码方式
1.通过string.getBytes(charsetNane)获得的字节数组,字节数组的编码方式,决定了FileOutStream写出文件的格式 例1:字节数组采用“GBK”编码,write生成的文 ...