初探 SpringBoot 自动装配

╰半橙微兮° 2023-02-21 12:43 4阅读 0赞

在不使用 SpringBoot 的时候,如果我们需要一个类必须要将它放到 Spring 的容器中,但是使用了 SpringBoot 之后就算我们不配置,仅仅是导入 jar 包就可以直接从容器中获取类了,这是怎么实现的呢?

基于 SpringBoot 2.2.0.RELEASE 版本

下面介绍帮助 SB 实现自动装配的 最关键 的四个注解或类。

SpringBootApplication

首先每个 SpringBoot 项目都会有一个启动类,来看一下这个启动类:

  1. @SpringBootApplication
  2. public class DemoApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(DemoApplication.class, args);
  5. System.out.println("http://localhost:8081/wsuo");
  6. }
  7. }

它上面放了一个注解 @SpringBootApplication 来表示他是一个启动类;

跟踪一下这个注解:

  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  4. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  5. public @interface SpringBootApplication {
  • @SpringBootConfiguration 表示这是一个配置类;
  • @EnableAutoConfiguration 表示开启自动配置,最重要的注解;
  • @ComponentScan 递归的扫描该类所在目录下的所有类,相当于 <context:component-scan>

EnableAutoConfiguration

继续跟踪一下这个注解:

  1. @AutoConfigurationPackage
  2. @Import(AutoConfigurationImportSelector.class)
  3. public @interface EnableAutoConfiguration {

该注解下又有两个新的的注解:

  • @AutoConfigurationPackage 是一个标志,表示该类所在的包应该被注册;
  • @Import 表示导入组件类;

AutoConfigurationImportSelector

继续跟踪 AutoConfigurationImportSelector 类,观察到该类的第一个方法就是 selectImports ,意思是 选择导入
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70
也就是说,我这个类的名字叫做 自动配置导入选择器,既然是选择,肯定要有一个方法选择,这个方法就是 selectImports ,作用是选择导入哪些类。

该方法又调用了该类中的其他方法,最终会到这里:
20200630185604227.png
这个方法的返回值是一个 List 集合,它里面装的是 候选的 配置类全限定类名,也就是确定哪些类要被自动装配了。

可以看到这里调用了 SpringFactoriesLoader 的静态方法 loadFactoryNames,说明我们的这些全限定类名都是 SpringFactoriesLoader 带过来的。

SpringFactoriesLoader

跟踪 SpringFactoriesLoader 类:
20200630190201457.png
开屏雷击,直接一个 final 指向 "META-INF/spring.factories"

我们先不看这个,先找到之前用的那个方法,发现那个方法又调用了这个方法 loadSpringFactories
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70 1
该方法首先去刚才看到的那个路径下获取了资源,然后放到一个集合中,接着使用 while 遍历,边遍历边将里面的东西放进一个 Properties 类中,随后又转为 Map,而我们之前用的 loadFactoryNames 就是将 Map 又转为了 List

再来看那个文件是什么?
20200630190927338.png
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70 2
打开之后发现都是全限定类名。

真相大白

到此为止终于知道了,原来它先去加载这个 spring.factories 文件,这里面全是全限定类名,也就是候选人,这些类即将被加载到容器中,然后经过一系列的变化,比如从 MapList 再到 String,最后通过 Class.forName() 加载类到容器中。

但是又有一个问题来了,这些类那么多,就算我这个项目中用不到也会被加载到容器中吗? 那这样岂不是太浪费了。

新的思考

确实,如果用不到那我加载它干嘛?

就算我们笨想也能想到,SpringBoot 肯定做了限制,也就是说只能是满足一定条件的类才能进来,才有资格成为 候选人

那条件是啥,怎么控制的?

我们在 spring.factories 文件中随便找一个类进去看看,比如 org.springframework.boot.autoconfigure.web.servlet 下的 HttpEncodingAutoConfiguration 类。

  1. @Configuration(proxyBeanMethods = false)
  2. @EnableConfigurationProperties(HttpProperties.class)
  3. @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
  4. @ConditionalOnClass(CharacterEncodingFilter.class)
  5. @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
  6. public class HttpEncodingAutoConfiguration {

该类有三个 Conditional ,就是条件的意思,分别是:

  • @ConditionalOnWebApplication
  • @ConditionalOnClass
  • @ConditionalOnProperty

他们的区别参照下图:
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70 3
就拿 @ConditionalOnClass(CharacterEncodingFilter.class) 来说,他的意思是,只有系统中有 CharacterEncodingFilter 类时才生效。

再来看这个类的一个方法:
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70 4
@ConditionalOnMissingBean 表示容器中没有这个 Bean 时才加入到容器中。

总结

SpringBoot 去那个全是全限定类名的文件中加载类的时候,会先看看这个类上面的条件是否全部满足了,只有全部满足条件了才把他作为候选人。

我们在 pom 文件中导入对应的 jar 包就会使相应的类满足条件,这样他就能自动配置了,所以还是取决于我们导入的包,如果导入了相应的包,那么相应的自动配置类就会被作为候选人自动配置。

还有一点要说的就是在这个类下有一个 HttpProperties
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70 5
这种 **Properties 每个自动配置类都有,它对应于你在 properties.yml 文件中可以配置的信息。

比如在这个类下有全局变量 logRequestDetailsencoding,那么你就能在配置文件中配置,如果不配它就用默认值。
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk0MTM2NA_size_16_color_FFFFFF_t_70 6
其中这个是它的指定前缀:
20200630193101233.png
对应的配置如下,其中 encoding 是个对象:
20200630193212230.png

发表评论

表情:
评论列表 (有 0 条评论,4人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Springboot自动装配

    一.自动装配 > 自动装配是springboot的核心,一般提到自动装配就会和springboot联系在一起。实际上 Spring Framework 早就实现了这个功能

    相关 SpringBoot自动装配

    微服务: 一个大的项目可以由多个业务模块构成(微服务),每一个业务模块可以作为一个项目,每个项目直接通过HTTP接口进行调用。spring boot可以快速开发微服务,即