初探 SpringBoot 自动装配
在不使用 SpringBoot
的时候,如果我们需要一个类必须要将它放到 Spring
的容器中,但是使用了 SpringBoot
之后就算我们不配置,仅仅是导入 jar
包就可以直接从容器中获取类了,这是怎么实现的呢?
基于
SpringBoot 2.2.0.RELEASE
版本
下面介绍帮助 SB
实现自动装配的 最关键 的四个注解或类。
SpringBootApplication
首先每个 SpringBoot
项目都会有一个启动类,来看一下这个启动类:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("http://localhost:8081/wsuo");
}
}
它上面放了一个注解 @SpringBootApplication
来表示他是一个启动类;
跟踪一下这个注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@SpringBootConfiguration
表示这是一个配置类;@EnableAutoConfiguration
表示开启自动配置,最重要的注解;@ComponentScan
递归的扫描该类所在目录下的所有类,相当于<context:component-scan>
;
EnableAutoConfiguration
继续跟踪一下这个注解:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
该注解下又有两个新的的注解:
@AutoConfigurationPackage
是一个标志,表示该类所在的包应该被注册;@Import
表示导入组件类;
AutoConfigurationImportSelector
继续跟踪 AutoConfigurationImportSelector
类,观察到该类的第一个方法就是 selectImports
,意思是 选择导入:
也就是说,我这个类的名字叫做 自动配置导入选择器,既然是选择,肯定要有一个方法选择,这个方法就是 selectImports
,作用是选择导入哪些类。
该方法又调用了该类中的其他方法,最终会到这里:
这个方法的返回值是一个 List
集合,它里面装的是 候选的 配置类 的 全限定类名,也就是确定哪些类要被自动装配了。
可以看到这里调用了 SpringFactoriesLoader
的静态方法 loadFactoryNames
,说明我们的这些全限定类名都是 SpringFactoriesLoader
带过来的。
SpringFactoriesLoader
跟踪 SpringFactoriesLoader
类:
开屏雷击,直接一个 final
指向 "META-INF/spring.factories"
;
我们先不看这个,先找到之前用的那个方法,发现那个方法又调用了这个方法 loadSpringFactories
:
该方法首先去刚才看到的那个路径下获取了资源,然后放到一个集合中,接着使用 while
遍历,边遍历边将里面的东西放进一个 Properties
类中,随后又转为 Map
,而我们之前用的 loadFactoryNames
就是将 Map
又转为了 List
。
再来看那个文件是什么?
打开之后发现都是全限定类名。
真相大白
到此为止终于知道了,原来它先去加载这个 spring.factories
文件,这里面全是全限定类名,也就是候选人,这些类即将被加载到容器中,然后经过一系列的变化,比如从 Map
到 List
再到 String
,最后通过 Class.forName()
加载类到容器中。
但是又有一个问题来了,这些类那么多,就算我这个项目中用不到也会被加载到容器中吗? 那这样岂不是太浪费了。
新的思考
确实,如果用不到那我加载它干嘛?
就算我们笨想也能想到,SpringBoot
肯定做了限制,也就是说只能是满足一定条件的类才能进来,才有资格成为 候选人 。
那条件是啥,怎么控制的?
我们在 spring.factories
文件中随便找一个类进去看看,比如 org.springframework.boot.autoconfigure.web.servlet
下的 HttpEncodingAutoConfiguration
类。
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
该类有三个 Conditional
,就是条件的意思,分别是:
@ConditionalOnWebApplication
@ConditionalOnClass
@ConditionalOnProperty
他们的区别参照下图:
就拿 @ConditionalOnClass(CharacterEncodingFilter.class)
来说,他的意思是,只有系统中有 CharacterEncodingFilter
类时才生效。
再来看这个类的一个方法:@ConditionalOnMissingBean
表示容器中没有这个 Bean
时才加入到容器中。
总结
当 SpringBoot
去那个全是全限定类名的文件中加载类的时候,会先看看这个类上面的条件是否全部满足了,只有全部满足条件了才把他作为候选人。
我们在 pom
文件中导入对应的 jar
包就会使相应的类满足条件,这样他就能自动配置了,所以还是取决于我们导入的包,如果导入了相应的包,那么相应的自动配置类就会被作为候选人自动配置。
还有一点要说的就是在这个类下有一个 HttpProperties
:
这种 **Properties
每个自动配置类都有,它对应于你在 properties.yml
文件中可以配置的信息。
比如在这个类下有全局变量 logRequestDetails
和 encoding
,那么你就能在配置文件中配置,如果不配它就用默认值。
其中这个是它的指定前缀:
对应的配置如下,其中 encoding
是个对象:
还没有评论,来说两句吧...