本文将从三个问题出发,详解springboot的自动装配原理
1.Springboot的自动装配是什么?
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。自动装配帮助我们简化了外部jar包使用的配置操作,在spring中,如果我们引入一个第三方依赖,需要手动对其进行配置,如Redis,则需要在Spring中为其编写一个特定的bean,但是在springboot中,我们只需简单的引入Redis对应的的starter依赖即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.Springboot自动装配的实现原理?
我们在idea中新建一个springboot项目,点击主启动类的 @SpringBootApplication。
点击之后发现,@SpringBootApplication注解主要由以下几个注解组成,
我们只需重点关注其中的三个注解:
- SpringBootConfiguration 的内部是 Configuration: 允许在上下文中注册bean或引入其他的配置;
- ComponentScan: 扫描被@Component注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描的bean,如图中的TypeExcludeFilter.class 和 AutoConfigurationExcludeFilter.class;
- EnableAutoConfiguration: 自动装配实现的核心注解。
我们继续查看 EnableAutoConfiguration 其中到底是如何实现的,点击进去查看源码。
可以看到,红框标出的部分,通过@Import注解引入了 AutoConfigurationImportSelector.class,这个类就是自动装配的关键。
通过这两张图,可以看出AutoConfigurationImportSelector 实现了 ImprotSelector接口,并实现该接口中的 selectImports 方法。该方法的功能是: 获取所有符合条件的类的全限定类名,将这些类加载到IOC容器中去。
在selectImports方法中,可以看到另一个关键方法: getAutoConfigurationEntry ,该方法的源码如下,执行过程请看注释:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 判断自动装配开关是否打开
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 获取 exclude 和 excludeName
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 读取META-INF/spring.factories,获取需要自动装配的所有配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 获取@ConditionalOnXXX 中的所有条件
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
spring.factories中这么多配置,每次启动并不是全部加载。需要一遍筛选,@ConditionalOnXXX 中的所有条件都满足,该类才会生效。
Spring Boot 提供的条件注解如下
- @ConditionalOnBean:当容器里有指定 Bean 的条件下
- @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
- @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
- @ConditionalOnClass:当类路径下有指定类的条件下
- @ConditionalOnMissingClass:当类路径下没有指定类的条件下
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
到此,原理部分就结束了,那么如何自定义一个starter呢?
3.自定义一个starter
首先,我们创建一个Maven项目,在pom文件中引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
在resources目录下创建一个spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.ThreadPoolConfiguration
编写模块类:
@Configuration
public class ThreadPoolConfiguration {
@Bean
@ConditionalOnClass(ThreadPoolExecutor.class)
public ThreadPoolExecutor getExecutor() {
return new ThreadPoolExecutor(10,10,10, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
}
}
到此,基本模块已经构建,安装maven到本地:
新创建一个springboot项目,引入我们刚才编写好的依赖:
<dependency>
<groupId>org.example</groupId>
<artifactId>threadpool-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
在测试类中,测试是否引入生效。
@SpringBootTest
class TestspringApplicationTests {
@Autowired
ThreadPoolConfiguration threadPoolExecutorTest;
@Test
void contextLoads() {
System.out.println("threadPoolExecutorTest.getExecutor().getCorePoolSize() = " + threadPoolExecutorTest.getExecutor().getCorePoolSize());
}
}
正常输出! 至此Springboot的自动装配原理讲述完毕,觉得还可以的麻烦动动小手点点赞。
本文参考链接:https://github.com/Snailclimb/JavaGuide