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 容器中。
@Override
public 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