阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

深入Spring Boot自动装配

380次阅读
没有评论

共计 8759 个字符,预计需要花费 22 分钟才能阅读完成。

1、Spring Boot 自动装配原理

依赖 @Enable 模块驱动设计模式,@EnableAutoConfiguration 必然会“@Import”ImportSelector 或 ImportBeanDefinitionRegister 的实现类, 查看源码:

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {......}

其中 AutoConfigurationImportSelector 就是 @EnableAutoConfiguration 所需 ”@Import” 的 DeferredImportSelector 实现类,由于 DeferredImportSelector 作为 ImportSeldector 的子接口,所以组件自动装配逻辑均在 selectImports 方法中.

源码:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ...... @Override // 根据 autoConfigurationMetadata 条件执行过滤 public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS; } // 加载自动装配的元信息 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); // 获取自动装配的类名集合 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY; } // 获取 @EnableAutoConfiguration 标注类的元信息 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); // 根据 autoConfigurationMetadata 条件执行过滤 configurations = filter(configurations, autoConfigurationMetadata); // 自动装配的导入事件 fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }

解析:

  • AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    .loadMetadata(this.beanClassLoader); 加载自动装配的元信息
  • AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
    annotationMetadata); 获取自动装配信息
  • AnnotationAttributes attributes = getAttributes(annotationMetadata); 获取 @EnableAutoConfiguration 标注类的元信息
  • List configurations = getCandidateConfigurations(annotationMetadata, attributes); 获取自动装配的候选类名集合
  • configurations = removeDuplicates(configurations); 移除重复对象
  • Set exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions); 获取自动装配黑名单
  • configurations = filter(configurations, autoConfigurationMetadata); 根据 autoConfigurationMetadata 条件执行过滤
  • fireAutoConfigurationImportEvents(configurations, exclusions); 自动装配的导入事件

1.1、@EnableAutoConfiguration 读取侯选装配组件

源码:

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; } // 获取 Spring Factories 加载工厂类 protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class; } // 通过名字讲行加载 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } // 装载 Spring Factories 工厂机制 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 从缓存中获取 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) {return result; } else {try {// 搜索指定 Classpath 下所有的 META-INF/spring.factories 资源内容 Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); // 循环遍历 spring.factories 资源内容 while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); // 将资源内容制作为 properties 文件 Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); // 合并为一个 key 为接口全类名,value 是实现类全类名列表 while(var6.hasNext()) {Entry<?, ?> entry = (Entry)var6.next(); // 接口全类名 String factoryTypeName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) {// 实现类全类名 String factoryImplementationName = var9[var11]; result.add(factoryTypeName, factoryImplementationName.trim()); } } } // 放入缓存 cache.put(classLoader, result); return result; } catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }

1.2、@EnableAutoConfiguration 排除自动装配组件

源码:

// 获取要排除的自动装配组件 protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 用于存储排除名单 Set<String> excluded = new LinkedHashSet<>(); // 添加排除类,spring.autoconfigure.exclude excluded.addAll(asList(attributes, "exclude")); // 添加排除类名,spring.autoconfigure.excludeName excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); // 添加所有的自动装配排除集合, 用于后面检查类名集合是否合法 excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; }

1.3、@EnableAutoConfiguration 过滤自动装配组件

源码:

// 获取要过滤的自动装配组件 private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = System.nanoTime(); // 将自动装配类名转化为字符串数组 String[] candidates = StringUtils.toStringArray(configurations); boolean[] skip = new boolean[candidates.length]; boolean skipped = false; for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {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; } } } // 如果不需要过滤, 直接返回 if (!skipped) {return configurations; } List<String> result = new ArrayList<>(candidates.length); // 如果需要过滤, 将过滤后的对象放入 result 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); } // 获取由所有 ImportFilters 集合 protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }

2、自定义 Spring Boot Starter

2.1、新添加 Maven 工程

pom.xml 文件如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tyschool</groupId> <artifactId>formatter-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.6.RELEASE</version> <optional>true</optional> </dependency> </dependencies> </project>

2.2、新建格式化接口

public interface Formatter {String format(Object obj); }

2.3、实现接口

public class DefaultFormatter implements Formatter {public String format(Object obj) {return String.valueOf(obj); } }

2.4、实现 DefaultFormatter 自动装配

@Configuration public class FormatterAutoConfiguration {@Bean public Formatter defaultFormatter(){return new DefaultFormatter();} }

2.5、在 META-INF/spring.factories 资源声明 FormatterAutoConfiguration

# FormatterAutoConfiguration 自动装配声明
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tyschool.autoconfigure.config.FormatterAutoConfiguration

2.6、构建 Spring Boot Starter

mvn -Dmaven.test.skip -U clean install

2.7、添加 formatter-spring-boot-starter 依赖

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.6.RELEASE</version> <optional>true</optional> </dependency> <dependency> <groupId>com.tyschool</groupId> <artifactId>formatter-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>

2.8、新建引导类

@SpringBootApplication public class FormatterBootStrap {public static void main(String[] args) {// 创建 Spring 上下文 ConfigurableApplicationContext context = new SpringApplicationBuilder(FormatterBootStrap.class) .web(WebApplicationType.NONE) .run(args); Map<String, Object> map = new HashMap<String, Object>(); map.put("name","tyschool"); // 获取 bean Formatter formatter = context.getBean(Formatter.class); // 格式化数据并输出 System.out.println(formatter.format(map)); context.close();} }

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-06-06发表,共计8759字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19348
评论数
4
阅读量
7824820
文章搜索
热门文章
开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南

开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南

开发者必备神器:阿里云 Qoder CLI 全面解析与上手指南 大家好,我是星哥。之前介绍了腾讯云的 Code...
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
云服务器部署服务器面板1Panel:小白轻松构建Web服务与面板加固指南

云服务器部署服务器面板1Panel:小白轻松构建Web服务与面板加固指南

云服务器部署服务器面板 1Panel:小白轻松构建 Web 服务与面板加固指南 哈喽,我是星哥,经常有人问我不...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
使用1Panel面板搭建属于你的AI项目环境

使用1Panel面板搭建属于你的AI项目环境

使用 1Panel 面板搭建属于你的 AI 项目环境 在 AI 项目越来越火的今天,很多朋友都想自己动手搭建一...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...
CSDN,你是老太太喝粥——无齿下流!

CSDN,你是老太太喝粥——无齿下流!

CSDN,你是老太太喝粥——无齿下流! 大家好,我是星哥,今天才思枯竭,不写技术文章了!来吐槽一下 CSDN。...
在Windows系统中通过VMware安装苹果macOS15

在Windows系统中通过VMware安装苹果macOS15

在 Windows 系统中通过 VMware 安装苹果 macOS15 许多开发者和爱好者希望在 Window...
星哥带你玩飞牛NAS-2:飞牛配置RAID磁盘阵列

星哥带你玩飞牛NAS-2:飞牛配置RAID磁盘阵列

星哥带你玩飞牛 NAS-2:飞牛配置 RAID 磁盘阵列 前言 大家好,我是星哥之前星哥写了《星哥带你玩飞牛 ...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择

星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择

星哥带你玩飞牛 NAS-12:开源笔记的进化之路,效率玩家的新选择 前言 如何高效管理知识与笔记,已经成为技术...
星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛 NAS 硬件 02:某鱼 6 张左右就可拿下 5 盘位的飞牛圣体 NAS 前言 大家好,我是星...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...
告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

  告别 Notion 焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁” 引言 在数字笔记工...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...