springboot自动装配原理

news/2025/3/15 5:55:37/

引言

springboot的自动装配是其重要特性之一,在使用中我们只需在maven中引入需要的starter,然后相应的Bean便会自动注册到容器中。例如:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
@Resource
private RabbitTemplate rabbitTemplate;

此时我们便可以注入rabbitTemplate使用相应的功能了。本篇文章将探究springboot如何实现的自动装配(基于2.2.5.RELEASE)。

@EnableAutoConfiguration

在@SpringBootAppilication的注解上有一个@EnableAutoConfiguration注解,这个注解完成了自动装配的功能。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};}

@EnableAutoConfiguration通过@Import注解将AutoConfigurationImportSelector中selectImports方法返回的类注册到容器中。@EnableAutoConfiguration 会尝试猜测并配置那些你需要的bean。通常根据classpath和你定义的bean来决定注册哪些类。例如,如果你的classpath有tomcat-embed相关的jar,那么你就可能想要TomcatServletWebServerFactory这个类的实例。
因为当使用@SpringBootApplication时自动装配就会自动生效,所以再使用这个注解将不会再起作用。
自动装配bean的过程将在用户自定义的bean被注册到容器之后进行
每个starter对应一个XXXAutoConfiguration类,其中定义了一些需要的bean

如何不让某些类自动装配

  1. 可以通过exclude属性将那些不想自动装配的类排除在外
  2. 如果你不能访问那些想排除的类可以使用exclueName属性指定类全名
  3. 也可以使用spring.autoconfigure.exclude.property属性

AutoConfigurationImportSelector

selectImports()

将selectImports方法重写为:

@Override
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());
}
  1. 入参AnnotationMetadata里面记录着@Import注解的元数据,例如该注解所标记的类
  2. 判断自动装配是否关闭,如果关闭就返回空数组,自动装配可以通过spring.boot.enableautoconfiguration=false关闭,默认是开启,关闭后将无法自动装配
  3. 加载AutoConfigurationMetadata,其实就是将META-INF/spring-autoconfigure-metadata.properties文件下的属性加载进来,过滤时使用。

存储着待自动装配候选类的过滤计算规则,框架会根据文件中的规则逐个判断候选类是否需要自动装配进容器 作用TODO

  1. 调用getAutoConfigurationEntry获取需要加载入容器的类

getAutoConfigurationEntry()

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,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 = filter(configurations, autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}
  1. 获得@EnableAutoConfiguration注解的属性,即exclude和excludeName属性的值
  2. 获得那些可能会被考虑的XXXAutoConfiguration类的类名,从META-INF/spring.factory的org.springframework.boot.autoconfigure.EnableAutoConfiguration属性获得
  3. 将获得的XXXAutoConfiguration类名去重
  4. 去掉1中获得的exclude和excludeName属性中的类,另外将配置文件中的spring.autoconfigure.exclude属性配置的类也去掉
  5. 过滤
  6. 触发自动配置事件

filter()

过滤即是通过规则将待注入的AutoConfiguration类进行筛选,将符合条件的留下。过滤的过程如下:

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);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);
}
  1. 入参configurations为待过滤的所有AutoConfigration类,autoConfigurationMetadata为过滤规则
  2. getAutoConfigurationImportFilters()找出spring.factory中org.springframework.boot.autoconfigure.AutoConfigurationImportFilter对用的属性值作为过滤器,包括以下三个:
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

如果有哪个XXAutoConfiguration类没有通过过滤,则剔除出去。
下面将介绍这3个过滤器的作用:

  1. OnClassCondition 指定的类在已经被加载到jvm中,通过classLoader.loadClass(className)或Class.forName(className)获取到类即可
  2. OnBeanCondition 指定的bean已经被加载到容器中
  3. OnWebApplicationCondition 当是某种web应用时才能通过条件,目前都是SERVLET类型的web应用

以OnClassCondition为例,其核心代码为:

private ConditionOutcome getOutcome(String candidates) {try {if (!candidates.contains(",")) {return getOutcome(candidates, this.beanClassLoader);}for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);if (outcome != null) {return outcome;}}
}
catch (Exception ex) {// We'll get another chance later
}
return null;
}
private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {if (ClassNameFilter.MISSING.matches(className, classLoader)) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class).didNotFind("required class").items(Style.QUOTE, className));}return null;
}

getOutCome即是获取匹配结果的方法,最终通过ClassNameFilter.MISSING.matches(className, classLoader)使用反射获得是否有类加载到jvm中

protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {if (classLoader != null) {return classLoader.loadClass(className);}return Class.forName(className);
}

总结

springboot的自动装配本质上是通过@Import(AutoConfigurationImportSelector.class)注解将spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration属性下的XXXAutoConfig类加载到容器中,当然不是全部加载,要通过spring-autoconfigure-metadata.properties文件下


http://www.ppmy.cn/news/30230.html

相关文章

我就不信你还不懂HashSet/HashMap的底层原理

&#x1f4a5;注&#x1f4a5; &#x1f497;阅读本博客需备的前置知识如下&#x1f497; &#x1f31f;数据结构常识&#x1f31f;&#x1f449;1️⃣八种数据结构快速扫盲&#x1f31f;Java集合常识&#x1f31f;&#x1f449;2️⃣Java单列集合扫盲 ⭐️本博客知识点收录于…

Android 基础知识4-3.5 RadioButton(单选按钮)Checkbox(复选框)详解

一、RadioButton&#xff08;单选按钮&#xff09; 1.1、简介 RadioButton表示单选按钮&#xff0c;是button的子类&#xff0c;每一个按钮都有选择和未选中两种状态&#xff0c;经常与RadioGroup一起使用&#xff0c;否则不能实现其单选功能。RadioGroup继承自LinearLayout&a…

套接字实现TCP

套接字 套接字的意义就是客户端与服务器进行双向通信的端点&#xff0c;如果有不理解点上面套接字三字更近距离了解套接字。 网络套接字与客户连接的特定网络有关的服务端口号&#xff0c;这个端口号允许linux进入特定的端口号的连接转到正确的服务器进程。 套接字通信的建立过…

【C++】string的使用及其模拟实现

文章目录1. STL的介绍1.1 STL的六大组件1.2 STL的版本1.3 STL的缺陷2. string的使用2.1 为什么要学习string类&#xff1f;2.2 常见构造2.3 Iterator迭代器2.4 Capacity2.5 Modifiers2.6 String operations3. string的模拟实现3.1 构造函数3.2 拷贝构造函数3.3 赋值运算符重载和…

cmd窗口中java命令报错。错误:找不到或无法加载主类 java的jdk安装过程中踩过的坑

错误: 找不到或无法加载主类 HelloWorld 遇到这个问题时&#xff0c;我尝试过网上其他人的做法。有试过添加classpath&#xff0c;也有试过删除classpath。但是依然报错&#xff0c;这里javac可以编译通过&#xff0c;说明代码应该是没有问题的。只是在运行是出现了错误。我安装…

EdgeYOLO学习笔记

EdgeYOLO学习笔记 EdgeYOLO: An Edge-Real-Time Object Detector Abstract 本文基于最先进的YOLO框架&#xff0c;提出了一种高效、低复杂度、无锚的目标检测器&#xff0c;该检测器可以在边缘计算平台上实时实现。为了有效抑制训练过程中的过拟合&#xff0c;我们开发了一种…

MyBatis常用的俩种分页方式

1、使用 limit 实现分页 select * from xxx limit m,n # m 表示从第几条数据开始&#xff0c;默认从0开始 # n 表示查询几条数据 select * from xxx limit 2,3 # 从索引为2的数据开始&#xff0c;往后查询三个。2、3、4 (1) 创建分页对象&#xff0c;用来封装分页的数据 PS…

路径规划-人工势场法

一.基本思想 目标点对机器人产生吸引力&#xff0c;障碍物对机器人产生排斥力&#xff1b; 所有力的合成构成机器人的控制律 二. 主要步骤 1.构建人工势场 目标点&#xff1a;吸引势场 障碍物&#xff1a;排斥势场 2.根据人工势场计算力 对势场求偏导 3.计算合力 计…