主要面向 Spring Boot 介绍。
从 Spring3.0 开始,@Configuration 可用于定义配置类,可替换 xml 配置文件,配置类中声明了一个或多个被 @Bean 注解的方法,Spring 容器会处理这些方法以构建 bean 定义和处理 bean 的请求。
配置类会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext (web 环境)处理[1],SpringBoot Web 环境下由 AnnotationConfigServletWebServerApplicationContext 处理配置类。
Spring 配置类用来注册 bean,它效果与 xml 文件是一样的,只是创建 Spring IoC 容器的方式不同:
//通过xml文件创建springIOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-beans.xml");
//通过配置类创建springIOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);如果一个类中有以下任意注解之一就属于配置类:
- 类上有
@Configuration注解 - 类上有
@Compontent注解 - 类上有
@CompontentScan注解 - 类上有
@Import注解 - 类上有
@ImportResource注解 - 类中有
@Bean标注的方法
判断一个类是否为一个配置类,可以看一下 org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate 方法,Spring 中处理这个过程会循环进行,直到完成所有配置类的解析及所有bean的注册。
@Configuration 的 Java Doc 知道 @Configuration 由 @Component 注解,因此被 @Configuration 注解的类会被自动检测(auto-detection)处理。
@Configuration is meta-annotated with @Component, therefore @Configuration classes are candidates for component scanning (typically using Spring XML's context:component-scan/ element) and therefore may also take advantage of @Autowired/@Inject like any regular @Component.
ConfigurationClassPostProcessor 负责解析和处理被 @Configuration 注解的类(更具体的讲是 postProcessBeanDefinitionRegistry 方法)。
BeanFactoryPostProcessor used for bootstrapping processing of
@Configurationclasses.
Registered by default when using<context:annotation-config/>or<context:component-scan/>. Otherwise, may be declared manually as with any other BeanFactoryPostProcessor.
This post processor is priority-ordered as it is important that any Bean methods declared in@Configurationclasses have their corresponding bean definitions registered before any other BeanFactoryPostProcessor executes.
这里先埋下一个知识点: ConfigurationClassPostProcessor 需要配置 <context:annotation-config/> 、<context:component-scan/>,或者手动声明 <bean class='org.springframework.context.annotation.ConfigurationClassPostProcessor'/>。
ConfigurationClassPostProcessor 首先会判断在 ApplicationContext 中的 bean 是否被 @Configuration 注解标记,然后使用 ConfigurationClassParser 来解析 @Configuration。
ConfigurationClassPostProcessor 解析配置类的大致流程:
- 使用
ConfigurationClassUtils.checkConfigurationClassCandidate检查 BeanDefinition 是否有@Configuration注解标记 - 对配置类进行排序
- 使用 ConfigurationClassParser 解析
@Configuration注解的信息 - 使用 ConfigurationClassBeanDefinitionReader 解析 BeanDefinition
其中 ConfigurationClassUtils.checkConfigurationClassCandidate 的判断逻辑与下列代码类似,AnnotatedElementUtils.isAnnotated 递归遍历所有注解:
//MyApplication 是启动类
AnnotatedElementUtils.isAnnotated(MyApplication.class, Configuration.class.getName());ConfigurationClassPostProcessor 又由谁来调用呢?
ApplicationContext 在 org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors 中触发 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 的调用。
具体是哪个 ApplicationContext 是由 org.springframework.boot.SpringApplication#createApplicationContext 判断并创建,这里以 SpringBoot 为例:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}在SpringBoot 中额外提供了一些配置类相关的注解,例如 SpringBoot 应用的入口启动类一般是如下形式:
@SpringBootApplication
public class MyApplication {
SpringApplication.run(MyApplication.class, args);
}在 SpringBoot 设计中 SpringBootConfiguration 来代替 Configuration,在使用 SpringBootApplication 时会自动带入 SpringBootConfiguration 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}@SpringBootApplication 由如下几个注解构成:
graph TD
A(SpringBootApplication)-->B1(SpringBootConfiguration)
A-->B2(EnableAutoConfiguration)
A-->B3(ComponentScan)
A-->B4(Inherited)
B1-->C1(Configuration)
C1-->C2(Component)
即一个 SpringBootApplication 注解具备 Configuration、ComponentScan、EnableAutoConfiguration 三者的功能:注册Bean、组件自动扫描、自动配置。
这里填下前文的一个知识点:ConfigurationClassPostProcessor 是需要配置的,例如在 xml 中添加 <context:component-scan/> 元素,是不是仅能通过 xml 配置呢? 答案自然是否定的。
这里引用下 @ComponentScan 的注释:
Configures component scanning directives for use with @Configuration classes. Provides support parallel with Spring XML's context:component-scan element.
Note that the<context:component-scan>element has an annotation-config attribute; however, this annotation does not. This is because in almost all cases when using @ComponentScan, default annotation config processing (e.g. processing @Autowired and friends) is assumed. Furthermore, when using AnnotationConfigApplicationContext, annotation config processors are always registered, meaning that any attempt to disable them at the @ComponentScan level would be ignored.
@ComponentScan 注解可以代替 <context:component-scan/> 配置,@ComponentScan 同样会注册 Annotation Config 相关的处理器。
我们结合前文中提及 @SpringBootApplication 同时被 @SpringBootConfiguration 和 @ComponentScan 两个注解所标记,这就可以理解在使用 SpringBoot 的过程中,我们通常无需关心 ConfigurationClassPostProcessor 的配置了。
Spring Boot 提供了 @EnableAutoConfiguration 用于激活自动配置(auto-configuration),简单理解 @Configuration 标识了 Spring 配置类,@ComponentScan 告诉 Spring 扫描哪些配置类,而 @EnableAutoConfiguration 则告诉 Spring 可以使用或者不使用哪些配置类。
我理解这三者分别表述了:配置类是什么?有哪些?用哪些?
@SpringBootApplication(scanBasePackages={},
exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class},)
public class MyApplication {}- scanBasePackages 属性对应 ComponentScan 的 basePackages 属性;
- exclude 属性对应 EnableAutoConfiguration 的 exclude 属性;
简单的实现了 ComponentScan、EnableAutoConfiguration 的组件扫描和自动配置的能力。
不论是 XML 或者配置类均是 ConfigurationClassPostProcessor 等处理的,配置类的扫描是按照 <context:component-scan/> 和 @ComponentScan 的 basePackages 属性值来取得扫描路径,从而扫描出 classpath 下所有需要解析的配置类。
如果需要被 Ioc 容器管理的 Bean 的路径不在 SpringBoot 的包扫描路径下怎么办呢,即如何加载第三方的 Bean 呢?
一种类似 spi 方式来引入配置类:spring.factories,该文件位于 classpatch:META-INF 目录中,里面的内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
elseifer.example.service.MyAutoConfiguraion
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ContextIdApplicationContextInitializer它不单单可以指定配置类,还可以指定其他 Spring 提供的扩展类,例如 ApplicationContextInitializer 等等。
在 SpringBoot Application 启动类(或者 Spring 配置类)上使用 @Import 注解,它的等价于 <import/>
@Import({MyConfig.class})
@SpringBootApplication
public class MyApplication {...}额外提及下 @ImportResource,同 @Import 的使用方式,用于加载 xml 配置,提供类似 xml 中的 标签。
@ImportResource({"classpath*:test/META-INF/example/*.xml"})
@SpringBootApplication
public class MyApplication {...}除了使用 EnableAutoConfiguration 和 SpringBootApplication 的 exclude 属性进行排除配置类,还可以通过配置文件指定参数的形式:
properties 文件指定
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationyml 文件指定:
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration到此,我们介绍了配置类、配置类的注解及其原理等内容,简单回顾:
- 配置类可以代替 xml 来注册 Bean,使用
@Configuration等注解声明配置类;@Configuation等价于<Beans></Beans>@Bean等价于<Bean></Bean>@ComponentScan等价于<context:component-scan/>
- 配置类由
ConfigurationClassPostProcessor负责解析; - 使用
@ComponentScan时 SpringBoot 将默认注册ConfigurationClassPostProcessor; - 第三方或者非默认路径的配置类需要引入时,可以使用
spring.factories、@Import;