SpringBoot 常用注解的原理和使用
@AutoConfiguration
读取所有jar包下的 /META-INF/spring.factories 并追加到一个 LinkedMultiValueMap 中。每一个url中记录的文件路径如下:
file:/C:/Users/wangchao/apache-maven-3.5.0/repo/com/baomidou/mybatis-plus-boot-starter/3.5.1/mybatis-plus-boot-starter-3.5.1.jar!/META-INF/spring.factories
按照如下路径查看
// 1. @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
// 2. AutoConfigurationImportSelector.class#selectImports()
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// 3. AutoConfigurationImportSelector.class#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);
}
// 4. AutoConfigurationImportSelector.class#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;
}
// 5. org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
最终使用 loadSpringFactories(@Nullable ClassLoader classLoader) 方法读取所有配置文件。
// 6. org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories()
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
// 读取所有jar包下的 /META-INF/spring.factories
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
tomcat的自动配置内置于springboot的autoconfiguration中。参考tomcat的自动配置 https://www.cnblogs.com/zhaokejin/p/15626392.html
mybatis-plus的配置没有被springboot包括。因此mybatis-stater中包含一个包mybatis-spring-boot-autoconfigure,这其中配置了需要自动配置的类。
因此我们也可以在自己的项目下新建 /META-INF/spring.factories ,并配置自动配置类。
@Import
@Import 用于导入配置类或需要前置加载的类。被导入的类会注册为Bean,可直接作为Bean被引用。它的 value 属性可以支持三种类型:
- 被
@Configuration修饰的配置类、或普通类(4.2版本之后可以)。 ImportSelector接口的实现。ImportBeanDefinitionRegistrar接口的实现。
@Import 的配置
@Configuration
@Import(value = {TestA.class, TestB.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class ConfigurationTest {
}
导入一个普通类
package com.example.ssmpdemo.entity;
public class TestA {
public void fun(){
System.out.println("testA");
}
}
导入一个配置类
package com.example.ssmpdemo.entity;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestB {
public void fun(){
System.out.println("testB");
}
}
通过实现 ImportSelector 接口
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.ssmpdemo.entity.TestC"};
}
}
通过重写 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions 方法。
import com.example.ssmpdemo.entity.TestD;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition root = new RootBeanDefinition(TestD.class);
registry.registerBeanDefinition("testD", root);
}
}
@ConfigurationProperties
- 支持常见的下划线、中划线和驼峰的转换。支持对象引导。比如:
user.friend.name代表的是user对象中的friend对象中的name - 需要有
set()方法 - 只添加
@ConfigurationProperties(prefix = "xxx")并不会生效,需要配合@Configuration让容器识别到。 @EnableConfigurationProperties(value = ConfigData.class )会将value中指定的类注册为Bean,可直接用@AutoWired引用。
- 定义一个类用来记录所有字段,并使用
@ConfigurationProperties(prefix = "xxx")将数据注入到ConfigData中。
package com.example.ssmpdemo.entity;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 用来记录Configuration的数据
* @author wangc
*/
@Data
@ConfigurationProperties(value = "spring.datasource.druid")
public class ConfigData {
private String driverClassName;
private String url;
private String username;
private String password;
}
# 对应的yml文件
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:5506/ssmpdemo?serverTimezone=UTC
username: root
password: xxxx
使用
@EnableConfigurationProperties(JDBCProperties.class)将ConfigData注册为Bean,并提供给ConfigurationTest使用 。可将ConfigData作为参数注入到构造函数和普通函数中。可以用以下方式引用被
@ConfigurationProperties(value = "spring.datasource.druid")修饰的ConfigData
- 可以直接把
ConfigData当成Bean使用/**
* 可直接被注入
*/
@Autowired
private ConfigData configData;
- 可以用构造函数传入进来
@Data
@Configuration
@EnableConfigurationProperties(value = ConfigData.class )
public class ConfigurationTest {
private ConfigData configData2;
/**
* 作为构造函数的参数注入
* @param data
*/
ConfigurationTest(ConfigData data){
this.configData2 = data;
}
- 也可以作为
@Bean的方法函数的参数。只有当前类(ConfigurationTest)才可/**
* 直接作为函数的参数
* @param data
* @return
*/
@Bean(name = "configData2")
HashMap<String, String> getBean(ConfigData data){
return new HashMap<>(0);
}
- 可以省略
ConfigData直接将字段注入到返回结果中。@Bean
@ConfigurationProperties(value = "spring.datasource.druid")
HashMap<String, String> getBean2(ConfigData data){
// 会自动为hashMap赋值,或使用set方法为对象赋值
return new HashMap<>();
}
EnableConfigurationProperties注解的内部如下,它导入了一个实现了 ImportBeanDefinitionRegistrar 接口的类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerInfrastructureBeans(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
// 获得@EnableConfigurationProperties的value指向的对象,并注册。
getTypes(metadata).forEach(beanRegistrar::register);
}
SpringBoot 常用注解的原理和使用的更多相关文章
- SpringBoot 常用注解(持续更新)
SpringBoot 常用注解 @SpringBootApplication @Bean @ComponentScan @ControllerAdvice @ExceptionHandler @Res ...
- SpringBoot 入门篇(二) SpringBoot常用注解以及自动配置
一.SpringBoot常用注解二.SpringBoot自动配置机制SpringBoot版本:1.5.13.RELEASE 对应官方文档链接:https://docs.spring.io/spring ...
- 接近8000字的Spring/SpringBoot常用注解总结!安排!
0.前言 大家好,我是 Guide 哥!这是我的 221 篇优质原创文章.如需转载,请在文首注明地址,蟹蟹! 本文已经收录进我的 75K Star 的 Java 开源项目 JavaGuide:http ...
- Spring/SpringBoot常用注解总结
转自:[Guide哥] 0.前言 可以毫不夸张地说,这篇文章介绍的 Spring/SpringBoot 常用注解基本已经涵盖你工作中遇到的大部分常用的场景.对于每一个注解我都说了具体用法,掌握搞懂,使 ...
- SpringBoot介绍,快速入门小例子,目录结构,不同的启动方式,SpringBoot常用注解
SpringBoot介绍 引言 为了使用ssm框架去开发,准备ssm框架的模板配置 为了Spring整合第三方框架,单独的去编写xml文件 导致ssm项目后期xml文件特别多,维护xml文件的成本也是 ...
- 菜鸟的springboot常用注解总结
菜鸟的springboot常用注解总结 0.前言 可以毫不夸张地说,这篇文章介绍的 Spring/SpringBoot 常用注解基本已经涵盖你工作中遇到的大部分常用的场景.对于每一个注解我都说了具体用 ...
- 结合参数接收响应转换原理讲解SpringBoot常用注解
一.常用注解回顾 1.1 @RequestBody与@ResponseBody //注意并不要求@RequestBody与@ResponseBody成对使用. public @ResponseBody ...
- SpringBoot常用注解的介绍及使用 - 转载
常用注解 @springBootApplication 系统启动类注解,此注解是个组合注解,包括了:@SpringBootConfiguration,@EnableAutoConfiguration, ...
- springboot系列五、springboot常用注解使用说明
一.controller相关注解 1.@Controller 控制器,处理http请求. 2.@RespController Spring4之后新加的注解,原来返回json需要@ResponseBod ...
随机推荐
- 一例智能网卡(mellanox)的网卡故障分析
一例智能网卡(mellanox)的网卡故障分析 背景:这个是在centos 7.6.1810的环境上复现的,智能网卡是目前很多 云服务器上的网卡标配,在oppo主要用于vpc等场景,智能网卡的代码随着 ...
- 详解GaussDB(DWS) 资源监控
摘要:本文主要着重介绍资源池资源监控以及用户资源监控. 本文分享自华为云社区<GaussDB(DWS)资源监控之用户.队列资源监控>,作者: 一只菜菜鸟. GaussDB(DWS)资源监控 ...
- [CF1500C] Matrix Sorting (模拟)
场上最后十几秒交上去过掉了耶! 题面 这里有两个 N ∗ M N*M N∗M 的 E x c e l \rm Excel Excel 表格 A A A 和 B B B. 我们知道 E x c e l ...
- MyBatis第一个程序
创建一个maven项目,并且在pom.xml导入myBatis和jdbc的jar包 <dependencies> <dependency> <groupId>org ...
- 记一次 .NET 某数控机床控制程序 卡死分析
一:背景 1. 讲故事 前段时间有位朋友微信上找到我,说它的程序出现了卡死,让我帮忙看下是怎么回事? 说来也奇怪,那段时间求助卡死类的dump特别多,被迫训练了一下对这类问题的洞察力 ,再次声明一下, ...
- KingbaseESV8R6临时表和全局临时表
临时表概述 临时表用于存放只存在于事务或会话期间的数据.临时表中的数据对会话是私有的,每个会话只能看到和修改自己会话的数据. 您可以创建全局(global)临时表或本地(locall)临时表. 下表列 ...
- PostgreSQL 时间函数分类与特性
KingbaseES 时间函数有两大类:返回事务开始时间和返回语句执行时的时间.具体函数看以下例子: 1.返回事务开始时的时间 以下函数返回事务开始的时间(通过 begin .. end 两次调用结果 ...
- Go常见
GO基础语法 方法或函数调用时,传入参数一般都是值复制,除非是map.slice.channel.指针类型是引用传递 短的变量声明(Short Variable Declarations),即自动推导 ...
- 跟羽夏学 Ghidra ——窗口
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- Mysqldump 的 的 6 大使用场景的导出命令
Mysqldump 选项解析 场景描述 1. 导出 db1.db2 两个数据库的所有数据. mysqldump -uroot -p -P8635 -h192.168.0.199 --hex-blob ...