SpringBoot自动装配
在讲之前先了解一下,手动装配的流程。
在没有Spring Boot的情况下,你需要手动配置和添加相关依赖,以实现类似于Spring Boot自动装配的功能。主要步骤:
-
引入Spring相关依赖: 首先,你需要引入Spring框架的相关依赖,包括核心容器(spring-context)、AOP(spring-aop)、数据访问(spring-jdbc、spring-orm)、事务管理(spring-tx)、Web(spring-web)等,具体依赖根据项目需求而定。
<!-- Spring Core Container --> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.10.RELEASE</version> <!-- 请替换为最新版本 --> </dependency><!-- Spring AOP --> <dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.10.RELEASE</version> </dependency><!-- 其他Spring相关依赖... -->
-
手动配置Spring配置文件: 在没有Spring Boot的情况下,你需要手动创建Spring的配置文件(例如XML配置文件)来定义和配置Spring容器中的Bean、数据源、事务管理等。
<!-- applicationContext.xml --> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置数据源、事务管理、其他Bean... --></beans>
-
手动扫描组件和配置类: Spring Boot自动扫描组件的特性在没有Spring Boot的情况下需要手动配置。你可以通过在配置文件中添加
<context:component-scan>
元素来启用组件扫描。<!-- applicationContext.xml --> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"xmlns:context="http://www.springframework.org/schema/context"><context:component-scan base-package="com.example" /></beans>
-
手动配置Web应用(如果有): 如果你的项目是一个Web应用,你需要手动配置Servlet、Filter、Listener等。
<!-- web.xml --> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><!-- 配置Servlet、Filter、Listener等... --></web-app>
而在SpringBoot中,你不需要写这一大堆的xml配置文件,很多配置只需要通过Spring Boot 的全局配置文件 application.properties
或application.yml
进行修改,比如更换端口号,配置 JPA 属性等等。
优点👌:因为Spring Boot提供了自动装配、默认配置和内嵌服务器等功能,让你更专注于业务逻辑的开发而不用过多关心底层配置。
进入正题
什么是SpringBoot自动装配?
Spring Boot 的自动装配(Auto-Configuration)原理是基于Spring Framework的条件化配置和@EnableAutoConfiguration
注解实现的。这种机制允许开发者在项目中引入相关的依赖,Spring Boot 将根据这些依赖自动配置应用程序的上下文和功能。
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的
META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
例如在SpringBoot项目中,引入Redis,只需要在Maven中,引入一个Redis的starter
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后在配置文件中写下Redis的配置就可以调用Redis的服务,这就是SpringBoot的强大之处
通俗来讲,自动装配就是通过注解或一些简单的配置就可以在SpringBoot的帮助下开启和配置各种功能,比如数据库访问、Web开发
SpringBoot自动装配原理
首先点进@SpringBootApplication注解的内部
让我们逐个解释这些注解的作用:
@Target({ElementType.TYPE})
: 该注解指定了这个注解可以用来标记在类上。在这个特定的例子中,这表示该注解用于标记配置类。@Retention(RetentionPolicy.RUNTIME)
: 这个注解指定了注解的生命周期,即在运行时保留。这是因为 Spring Boot 在运行时扫描类路径上的注解来实现自动配置,所以这里使用了RUNTIME
保留策略。@Documented
: 该注解表示这个注解应该被包含在 Java 文档中。它是用于生成文档的标记,使开发者能够看到这个注解的相关信息。@Inherited
: 这个注解指示一个被标注的类型是被继承的。在这个例子中,它表明这个注解可以被继承,如果一个类继承了带有这个注解的类,它也会继承这个注解。@SpringBootConfiguration
: 这个注解表明这是一个 Spring Boot 配置类。如果点进这个注解内部会发现与标准的@Configuration
没啥区别,只是为了表明这是一个专门用于 Spring Boot 的配置。@EnableAutoConfiguration
: 这个注解是 Spring Boot 自动装配的核心。它告诉 Spring Boot 启用自动配置机制,根据项目的依赖和配置自动配置应用程序的上下文。通过这个注解,Spring Boot 将尝试根据类路径上的依赖自动配置应用程序。@ComponentScan
: 这个注解用于配置组件扫描的规则。在这里,它告诉 Spring Boot 在指定的包及其子包中查找组件,这些组件包括被注解的类、@Component
注解的类等。其中的excludeFilters
参数用于指定排除哪些组件,这里使用了两个自定义的过滤器,分别是TypeExcludeFilter
和AutoConfigurationExcludeFilter
。
看下来,其实@SpringBootApplication可以看作是@Configuration、@EnableAutoConfiguration、@ComponentScan的组合
@EnableAutoConfiguration
这个注解是实现自动装配的核心注解
-
@AutoConfigurationPackage,将项目src中main包下的所有组件注册到容器中,例如标注了Component注解的类等等
-
@Import({AutoConfigurationImportSelector.class}),是自动装配的核心,👇就来分析一下它
AutoConfigurationImportSelector
AutoConfigurationImportSelector
是 Spring Boot 中一个重要的类,它实现了 ImportSelector
接口,用于实现自动配置的选择和导入。具体来说,它通过分析项目的类路径和条件来决定应该导入哪些自动配置类。
代码太多,选取部分主要功能的代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {// ... (其他方法和属性)// 获取所有符合条件的类的全限定类名,例如RedisTemplate的全限定类名(org.springframework.data.redis.core.RedisTemplate;),这些类需要被加载到 IoC 容器中。@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 扫描类路径上的 META-INF/spring.factories 文件,获取所有实现了 AutoConfiguration 接口的自动配置类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 过滤掉不满足条件的自动配置类,比如一些自动装配类configurations = filter(configurations, annotationMetadata, attributes);// 排序自动配置类,根据 @AutoConfigureOrder 和 @AutoConfigureAfter/@AutoConfigureBefore 注解指定的顺序sort(configurations, annotationMetadata, attributes);// 将满足条件的自动配置类的类名数组返回,这些类将被导入到应用程序上下文中return StringUtils.toStringArray(configurations);}// ... (其他方法)protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 获取自动配置类的候选列表,从 META-INF/spring.factories 文件中读取// 通过类加载器加载所有候选类List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());// 过滤出实现了 AutoConfiguration 接口的自动配置类configurations = configurations.stream().filter(this::isEnabled).collect(Collectors.toList());// 对于 Spring Boot 1.x 版本,还需要添加 spring-boot-autoconfigure 包中的自动配置类// configurations.addAll(getAutoConfigEntry(getAutoConfigurationEntry(metadata)));return configurations;}// ... (其他方法)protected List<String> filter(List<String> configurations, AnnotationMetadata metadata,AnnotationAttributes attributes) {// 使用条件判断机制,过滤掉不满足条件的自动配置类configurations = configurations.stream().filter(configuration -> isConfigurationCandidate(configuration, metadata, attributes)).collect(Collectors.toList());return configurations;}// ... (其他方法)protected void sort(List<String> configurations, AnnotationMetadata metadata,AnnotationAttributes attributes) {// 根据 @AutoConfigureOrder 和 @AutoConfigureAfter/@AutoConfigureBefore 注解指定的顺序对自动配置类进行排序configurations.sort((o1, o2) -> {int i1 = getAutoConfigurationOrder(o1, metadata, attributes);int i2 = getAutoConfigurationOrder(o2, metadata, attributes);return Integer.compare(i1, i2);});}// ... (其他方法)}
梳理一下😂,以下是 AutoConfigurationImportSelector
的主要工作:
- 扫描类路径: 在应用程序启动时,
AutoConfigurationImportSelector
会扫描类路径上的META-INF/spring.factories
文件,这个文件中包含了各种 Spring 配置和扩展的定义。在这里,它会查找所有实现了AutoConfiguration
接口的类,具体的实现为getCandidateConfigurations
方法。 - 条件判断: 对于每一个发现的自动配置类,
AutoConfigurationImportSelector
会使用条件判断机制(通常是通过@ConditionalOnXxx
注解)来确定是否满足导入条件。这些条件可以是配置属性、类是否存在、Bean是否存在等等。
例如:下面这个配置类,只有在RabbitTemplate.class, Channel.class存在才会去加载
@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}
有兴趣的童鞋可以详细了解下 Spring Boot 提供的条件注解
@ConditionalOnBean
:当容器里有指定 Bean 的条件下@ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下@ConditionalOnSingleCandidate
:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean@ConditionalOnClass
:当类路径下有指定类的条件下@ConditionalOnMissingClass
:当类路径下没有指定类的条件下@ConditionalOnProperty
:指定的属性是否有指定的值@ConditionalOnResource
:类路径是否有指定的值@ConditionalOnExpression
:基于 SpEL 表达式作为判断条件@ConditionalOnJava
:基于 Java 版本作为判断条件@ConditionalOnJndi
:在 JNDI 存在的条件下差在指定的位置@ConditionalOnNotWebApplication
:当前项目不是 Web 项目的条件下@ConditionalOnWebApplication
:当前项目是 Web 项 目的条件下
- 根据条件导入自动配置类: 满足条件的自动配置类将被导入到应用程序的上下文中。这意味着它们会被实例化并应用于应用程序的配置。
懂了上面这些,我们其实可以自己实现一个Starter,非常简单
自定义Starter
第一步,创建threadpool-spring-boot-starter
工程
第二步,引入 Spring Boot 相关依赖
第三步,创建ThreadPoolAutoConfiguration
第四步,在threadpool-spring-boot-starter
工程的 resources 包下创建META-INF/spring.factories
文件
最后新建工程引入threadpool-spring-boot-starter
测试通过!!!
总结
Spring Boot 通过@EnableAutoConfiguration
开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories
中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional
按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx
包实现起步依赖
希望这篇文章能够帮助你更好地理解SpringBoot自动装配,Happy coding! 🚀;点个关注,关注更多干货😘
参考链接:https://javaguide.cn/system-design/framework/spring/spring-boot-auto-assembly-principles.html