Spring Boot自动配置原理懂后轻松写一个自己的starter
目前很多Spring项目的开发都会直接用到Spring Boot。因为Spring原生开发需要加太多的配置,而使用Spring Boot开发很容易上手,只需遵循Spring Boot开发的约定就行了,也就是约定大于配置,无需觉得它神奇,它的底层都是使用的Spring。聊完这个原理带着大家轻松写一个自己的starter。
要学习它的自动配置原理首先自己要创建一个Spring Boot项目,创建项目过程很简单就不介绍了。学习它的自动配置原理,首先要看的就是如下这个注解(SpringBootApplication)。这个注解大家都是很熟悉,这个注解是由如下三个注解组成如下:
//第一个注解
@SpringBootConfiguration
//第二个注解
@EnableAutoConfiguration
//第三个注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
上面三个注解都是太太重要了,本文由于聊自动配置所以就只讲EnableAutoConfiguration这个注解,Spring Boot的自动配置原理精髓都在这个注解里面。好了那就先看这个注解的代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
看到这个注解一眼就能瞧到它帮助我们导入了一个AutoConfigurationImportSelector 。由于很多地方遇到了这个Import注解,所以先简单说一下这个注解的作用。
1:给Spring容器自动注入导入的类。如下使用,就是帮助Spring容器自动导入了一个TableEntity对象,在项目中你不需要new 对象,也不需要给这个对象加任何注解了,就可以直接使用TableEntity对象了。
@Configuration
@Import(TableEntity.class)
public class TestConfig {
}
2:给容器导入一个ImportSelector,比如上文讲的那个AutoConfigurationImportSelector 。通过字符串数组的方式返回配置类的全限定名,然后把这些类注入到Spring容器中,我们也就可以直接在Spring容器中使用这些类了。
好了讲了上面那2段作用我们主要分析的也就是下面这段代码了。
public class AutoConfigurationImportSelector {
@Override
//作用就是Spring会把这个方法返回的数组中所有全限定名的类注入到Spring容器中
//供使用者直接去使用这些类。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//这个方法是Spring Boot 自动配置要说的
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
然后我们后面主要分析的也就是getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata),这个方法。
//这个方法中的每个方法都很重要,一个一个说
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//1:见名知意,获取候选配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 2:去除重复的配置类,这个方法太好了。
configurations = removeDuplicates(configurations);
//3 :去除使用者排除的配置类。
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations这个方法的意思就是获取候选的配置类(也就是Spring Boot已经自动配置的那些类),如下:(PS我们一看那个报错信息就能猜出来Spring从这个【META-INF/spring.factories】下找配置类)
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;
}
主要找配置类信息的就是如下代码了。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
} private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 1:第一步先从缓存中找,找不到在循环遍历找,
// 由于Spring代码逻辑太复杂,Spring很多地方都采用这种缓存的设计
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
// public static final String 下面代码用到的常量值如下
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
try {
// 扫描 代码中的所有META-INF/spring.factories"文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
//循环遍历加载上面所说的文件下的文件,并把它们放入到
// LinkedMultiValueMap中
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
//放缓存中一份,后面要加载从这个缓存中直接取,
// 如果看全代码可知Spring Boot 缓存的不止有配置类,还有其他类。
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
从上面代码可知Spring主要是从META-INF/spring.factories文件中加载配置类,那么就带大家看一看Spring Boot自己已经配置的类有哪些。
后面就回到这个(removeDuplicates)去重方法,如下:
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
为什么要把这单独一行代码列出来呢?是因为我感觉这段去重复代码用的太好了,自从看了这段代码,后面博主自己写去重逻辑的时候也就参照Spring大佬这一行代码写去重逻辑(PS:如果自己业务去重逻辑没有其他逻辑的时候参考使用),简单,效率应该也不低毕竟大佬们这样用了。
后面代码逻辑就是一些去除用户自己要排除,要过滤掉的配置类。然后就会使用Spring的ImportSelector这个特性(PS具体Spring是怎么把这些返回权限定名的类加载的容器中的,是Spring加载类方面的知识,本文不做具体介绍)
好了,然后带着大家创建一个自己的starter(PS:命名规范我是参考了mybatis-spring,毕竟是大神们的命名规范,记好约定大于配置,哈哈哈)的starter 。
1: 创建一个工程,信息如下:
<groupId>scott-spring-boot-starter</groupId>
<artifactId>scottspringbootstarter</artifactId>
<version>0.0.1-SNAPSHOT</version>
2:再创建一个工程 也就是autoconfigure项目。
如下:
<groupId>com.spring.starter</groupId>
<artifactId>scott-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
在pom文件中引入如下(一般下面的是必须引入的):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath></relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
3:创建HelloService。
public class HelloService {
HelloProperties helloProperties ; public String sayHello(String name){
return helloProperties.getPrefix()+"-"+name+helloProperties.getSuffix() ;
}
public HelloProperties getHelloProperties() {
return helloProperties;
}
public void setHelloProperties(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}
} 4: 创建相应的properies文件。
@ConfigurationProperties(prefix="scott.hello") //以 scott.hello开头的。
public class HelloProperties { private String prefix ; private String suffix ;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
} 5:创建自定义的配置文件如下:
@Configuration
@ConditionalOnWebApplication // 在web环境下才生效
@EnableConfigurationProperties(HelloProperties.class) // 属性文件生效
public class HelloServiceAutoConfiguration {
@Autowired
HelloProperties helloProperties; @Bean
public HelloService helloService() {
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
};
}
6:在META-INF 文件夹下创建 spring.factories 文件,写入如下自己的配置类 。Spring Boot自动配置规约,约定大于规范,如下图的配置所示:
7:在scottspringbootstarter项目的pom文件中引入自定义的 autoconfigure如下:
<groupId>scott-spring-boot-starter</groupId>
<artifactId>scottspringbootstarter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.spring.starter</groupId>
<artifactId>scott-spring-boot-starter-autoconfigure</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency> </dependencies>
8:自定义starter就好了,然后就可以在我们自定义的工程中引入scottspringbootstarter就可以使用了。
如下使用方法,配置yml文件,然后使用对应的服务,So Easy:
如果感觉写的不错,欢迎转发,分享给其他感觉需要关注的人,谢谢。
下面问问ChatGPT一些问题。
Spring Boot自动配置原理懂后轻松写一个自己的starter的更多相关文章
- Spring Boot自动配置原理、实战
Spring Boot自动配置原理 Spring Boot的自动配置注解是@EnableAutoConfiguration, 从上面的@Import的类可以找到下面自动加载自动配置的映射. org.s ...
- Spring Boot自动配置原理与实践(二)
前言 在之前的博文(Spring Boot自动配置原理与实践(一))中,已经介绍了Spring boot的自动配置的相关原理与概念,本篇主要是对自动配置的实践,即自定义Starter,对原理与概念加深 ...
- Spring Boot自动配置原理(转)
第3章 Spring Boot自动配置原理 3.1 SpringBoot的核心组件模块 首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量: 我们cd到spring-boot- ...
- Spring Boot自动配置原理与实践(一)
前言 Spring Boot众所周知是为了简化Spring的配置,省去XML的复杂化配置(虽然Spring官方推荐也使用Java配置)采用Java+Annotation方式配置.如下几个问题是我刚开始 ...
- Springboot 系列(三)Spring Boot 自动配置原理
注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 关于配置文件可以配置的内容,在 Spring ...
- spring boot 自动配置原理
1).spring boot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration,先看一下启动类的main方法 public ConfigurableApplic ...
- Spring Boot 自动配置原理(精髓)
一.自动配置原理(掌握) SpringBoot启动项目会加载主配置类@SpringBootApplication,开启@EnableAutoConfiguration自动配置功能 @EnableAut ...
- Spring Boot自动配置原理
使用Spring Boot之后,一个整合了SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,这 是如何做到的? 一切魔力的开始,都是从我们的main函数来的,所以我们再次来 ...
- 【串线篇】spring boot自动配置原理
配置文件到底能写什么?怎么写?自动配置原理: 配置文件能配置的属性参照 一.自动配置原理: 1.1.SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfigur ...
- Spring Boot 自动配置原理是什么?
注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个 ...
随机推荐
- 9 STL-queue
重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦! 生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 写在前面,本篇章主要介绍S ...
- 记录一次从linux移动一个项目到windows遇到的问题
前言 这几天在linux平台写了一个垃圾软件,浪费了我10多天的时间,感觉很垃圾,然后我想在windows平台打包这个软件,然后出现了一个项目中有相同文件名的问题,导致一些文件相互覆盖 问题描述 我把 ...
- day28-jQuery01
jQuery01 参考文档1:jQuery API 中文文档 | jQuery API 中文在线手册 | jquery api 下载 | jquery api chm (cuishifeng.cn) ...
- supervisor安装与监控nginx
安装参考:https://www.cnblogs.com/zgcblog/p/10192077.html https://www.cnblogs.com/yangbo981205/p/14928897 ...
- 一个小时,200行代码,手写Spring的IOC、DI、MVC
一.概述 配置阶段:主要是完成application.xml配置和Annotation配置. 初始化阶段:主要是加载并解析配置信息,然后,初始化IOC容器,完成容器的DI操作,已经完成HandlerM ...
- 安装es可视化软件Kibana
一 Kibana介绍 Kibana 是一款开源的数据分析和可视化平台,它是 Elastic Stack 成员之一,设计用于和 Elasticsearch 协作. 您.可以使用 Kibana 对 Ela ...
- openresty package path
openresty lua_package_path 是整个openresty最基础的功能,不理解 path就无法做项目,更无法写框架. 先看下文档lua_package_path https://g ...
- Django重点及面试题
Django 简述python三大主流web框架 """ django 大而全,类似于航空母舰 但是有时候过于笨重 flask 小而精,类似于游骑兵(单行代码就可以起一个 ...
- npm Error: Cannot find module 'are-we-there-yet'
npm 损坏了,are-we-there-yet是npm所依赖的npmlog依赖的一个包,重新安装npm即可 踩坑,直接安装还是报错,不管执行哪个命令都是报下面这个错 网上百度了很多,有的说把node ...
- JavaScript:七大基础数据类型:数值number及其表示范围
数值number类型,用来表示任何类型的数字:整数或者浮点数都可以: 实际上,JS中的数值,是一个64位的浮点数,这与Java中的double类型的浮点数是一致的: 但是它有表示的范围,在范围内,JS ...