初探 SpringBoot 自动装配

时间:2022-07-22
本文章向大家介绍初探 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 文件,这里面全是全限定类名,也就是候选人,这些类即将被加载到容器中,然后经过一系列的变化,比如从 MapList 再到 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 文件中可以配置的信息。

比如在这个类下有全局变量 logRequestDetailsencoding,那么你就能在配置文件中配置,如果不配它就用默认值。

其中这个是它的指定前缀:

对应的配置如下,其中 encoding 是个对象: