什么是自动装配
这里的自动,指的是无需显示配置,装配则是指将第三方包中的类装配成Java Bean,最常见的使用场景就是我们引入的各种starter。
在Spring出现的很长一段时间,如果要将一个三方包中的类装配成一个Java Bean,我们需要一个一个显示进行装配 。
假设我们的工程三方包中有一个Person类,我们将其装配成一个Java Bean的代码大致如下。
- Person.java
public class Person {
private String name;
private Integer age;
}
- MyConfig.java
@Configuration
public class MyConfig {
@Bean
public Person person() {
Person person = new Person();
return person;
}
}
- xml文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc/ http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="xxx" />
<mvc:annotation-driven />
<!-- JSON Support -->
<bean name="person" class="com.cy.service.Person"/>
</beans>
而在我们平时使用的Spring Boot框架中,只需要引入第三方包提供的starter,就能完成相关类的自动装配。
我们以分页插件PageHelper为例,在gradle(或maven)中引入相关starter,就能完成相关类的Java Bean装配。
- build.gradl
implementation "com.github.pagehelper:pagehelper-spring-boot-starter:$pagehelperVersion"
自动装配原理
- SpringBoot 工程入口
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
-
PageHelper jar包
-
PageHelperAutoConfiguration
自动装配的关键在于注解@SpringBootApplication 与三方包中的文件 spring.factories。
@SpringBootApplication 是SpringBoot提供的一个核心注解,简化了在web项目开发中的诸多配置。
@SpringBootApplication由多个注解组成,其中最重要的三个是 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 。
-
@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制 -
@SpringBootConfiguration:允许在上下文中注册额外的 bean 或导入其他配置类 -
@ComponentScan: 扫描被@Component(@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。
@EnableAutoConfiguration是实现自动配置的核心注解,AutoConfigurationImportSelector是该注解的重要切面。
AutoConfigurationImportSelector实现了一个重要方法selectImports,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
这是源码中的一条核心调用链路:selectImports --> getAutoConfigurationEntry --> getCandidateConfigurations.
从Assert的提示中,我们已经可以看出这段代码必然和 spring.factories文件有着某种关联。
沿着 loadFactoryNames 往下走,我们最终会看到这么一行代码classLoader.getResources(FACTORIES_RESOURCE_LOCATION),这里常量FACTORIES_RESOURCE_LOCATION的值为
META-INF/spring.factories.
自此,我们从@SpringBootApplication注解出发,最终找到了加载spring.factories的相关代码。
稍微总结一下:
META-INF/spring.factories文件会包含一些配置类,这些配置类装配了相关Bean。- SpringBoot在启动时会扫描外部引用 jar 包中的
META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。 - 对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
自定义starter
了解完自动装配的基本原理,我们可以尝试自己来构建一个starter。
第一步:创建配置类
@Configuration
public class CustomHttpClientConfig {
@Bean
@ConditionalOnProperty(prefix = "custom",name = "createHttpClient",value = "true")
public Object specialHttpClient() {
OkHttpClient okHttpClient = new OkHttpClient();
return okHttpClient;
}
}
第二步:工程的resources 包下创建META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxxx.CustomHttpClientConfig
第三步:打包,此处省略打包命令.......
第四步:在另一个SpringBoot工程中引入该包,当
application.properties或application.yml中custom.createHttpClient为true时,会进行自动装配
拓展阅读(getAutoConfigurationEntry简析)
- 115行,获取
EnableAutoConfiguration注解中的exclude和excludeName - 116行,读取
META-INF/spring.factories,获取需要自动装配的所有配置类 - 117行--122行,根据条件注解,将不需要加载的类过滤掉(各种
@ConditionalOnXXX注解)
常见条件注解:
- @ConditionalOnBean:当容器里有指定 Bean 的条件下
- @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
- @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
- @ConditionalOnClass:当类路径下有指定类的条件下
- @ConditionalOnMissingClass:当类路径下没有指定类的条件下
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
- @ConditionalOnExpression:基于 SpEL 表达式作为判断条件
- @ConditionalOnJava:基于 Java 版本作为判断条件