@Configuration
所在包:org.springframework.context.annotation
所在 jar:spring-context.jar(5.1.6.RELEASE)
用于进行配置,修饰类,标示某一个类是配置类。
源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
标识一个 java 类声明了一个或者多个被 @Bean 注解标示的方法,而且可以被 Spring 的容器进行处理,生成 Bean 的定义,以及针对于运行期的 Bean 服务请求。
Java Config 实现方式
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
@Configuration 类通常是要么通过 AnnotationConfigApplicationContext ,要么通过 AnnotationConfigWebApplicationContext 来启动、实现的。
针对之前的代码的一个简单实例:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...
首先创造一个 AnnotationConfigApplicationContext 应用上下文的实例,接着把 Java Config 配置类注册到应用上下文当中,刷新应用上下文以后就可以通过 getBean 方法获取(从 Spring 容器内)之前定义好的 MyBean 实例,根据后面的请求,进行 MyBean 操作。
这也解释了,我们在 Spring Boot 应用当中,所有的 Java 配置类,怎么就能被 Spring Boot 或者 Spring 找到的。
Xml 实现方式
<beans>
<context:annotation-config/>
<bean class="com.acme.AppConfig"/>
</beans>
传统方式,在 Java Config 兴起以前,当时的标准就是使用 xml 来实现配置。在 Spring Boot 当中也是支持 xml 方式的,但是很少会这么用,通常都是使用注解方式。
<context:annotation-config/>
xml 方式必须要加上上面的命名空间,才能起效果。
@ComponentScan
组件扫描注解,修饰 @SpringBootApplication 注解。这个注解,主要是扫描应用的所有组件。
对于 Spring Boot 应用来说,常常会把包含 main 方法的这个类,也就是启动类,定义在一个包的最顶层。其他的控制器、服务层、数据层、组件层都定义在启动类的子包里。
因为根据注解扫描原则,注解会扫描这个包本身以及这个包的所有子包。所以一般 Spring Boot 的启动类都是位于整个应用的最顶层,应用的其他文件都在它的子包内。
使用环境 API
外部的值,可以通过把 org.springframework.core.env.Environment 组件注入到 @Configuration 所在的类当中,从而使用它们。
比如使用自动装配注解来注入:
@Autowired
自动装配注解,自动装配到所需的类里面。
@Configuration
public class AppConfig {
@Autowired
Environment env;
@Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setName(env.getProperty("bean.name"));
return myBean;
}
}
这里的 env 被 @Autowired 自动装配到 AppConfig 类里,而 myBean 的 name 值就是通过 env.getProperty("bean.name")
的方式来获取的外部值。
@PropertySource
属性源注解。通过环境对象(Environment)去解析的属性位于一个或者多个 属性源(property source)对象当中,而且被 @Configuration 修饰的类还可以通过使用 @PropertySource 注解贡献属性源(property source)到环境对象(Environment)中。
也就是说,只要通过 @PropertySource 指定了外部文件,那么就可以使用外部文件里面的值了。
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Inject
Environment env;
@Bean
public MyBean myBean() {
return new MyBean(env.getProperty("bean.name"));
}
}
例如上面的 bean.name
就是定义在 classpath:/com/acme/app.properties 外部文件里面的值。
@Value
值注解。外部的值可以通过值注解注入到 @Configuration 修饰的类中。
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Value("${bean.name}")
String beanName;
@Bean
public MyBean myBean() {
return new MyBean(beanName);
}
}
当实例化或者加载 AppConfig 类实例的时候,就会从 app.properties 外部配置当中,找到 bean.name 的配置信息,并把值设置给 beanName。
这种方式经常会与 Spring 的 PropertySourcesPlaceholderConfigurer 搭配使用。
@Import
导入注解。可以通过它来组合两个或者多个被 @Configuration 修饰的类,把多个配置类组合到一起,目的是实现配置的拆分。
类似于 Spring XML 配置方式中的 <import>
标签。
由于 @Configuration 对象,是做为一个 Spring Bean 对象在容器当中被管理的,因此被导入的 @Configuration 对象(配置)也是可以被注入的,比如通过构造器来注入。
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return DataSource
}
}
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
private final DatabaseConfig dataConfig;
public AppConfig(DatabaseConfig dataConfig) {
this.dataConfig = dataConfig;
}
@Bean
public MyBean myBean() {
// reference the dataSource() bean method
return new MyBean(dataConfig.dataSource());
}
}
DatabaseConfig 做为 AppConfig 的一个变量,通过构造方法注入进来,然后就可以使用 DatabaseConfig 相关的配置信息了。
启动
现在,不管是 AppConfig,还是被导入的 DatabaseConfig 都可以通过注册到 Spring Context 当中来启动。只需要注册 AppConfig 就可以了,因为 DatabaseConfig 已经被包含在 AppConfig 中了。
启动方法:给 Spring Context 注册导入的 @Configuration 类。
new AnnotationConfigApplicationContext(AppConfig.class);
@Profile
多环境注解。实际的项目当中,至少分为开发、测试、生产三种环境,每种环境的配置信息,应该都是不一样的,比如数据库、缓存等信息。
@Configuration 所修饰的类还可以被 @Profile 修饰,来指定不同环境信息。
@Profile("development")
@Configuration
public class EmbeddedDatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return embedded DataSource
}
}
@Profile("production")
@Configuration
public class ProductionDatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return production DataSource
}
}
上面的例子分别声明了开发阶段和生产阶段的数据源配置。
在 Spring Bean 的方法层次上,还可以声明一些 @Profile 的条件。
@Configuration
public class ProfileDatabaseConfig {
@Bean("dataSource")
@Profile("development")
public DataSource embeddedDatabase() { ... }
@Bean("dataSource")
@Profile("production")
public DataSource productionDatabase() { ... }
}
都是叫做 dataSource 的 Spring Bean,配置信息也在同一个 @Configuration 类当中,只是通过 @Profile 的值不同来区分。但是这是一个条件判断,当前环境如果是 development,DataSource Spring Bean 就是 embeddedDatabase,而当前环境如果是 production,DataSource Spring Bean 就是 productionDatabase。
上下两种方式,本质上都是一样的,只是上面的配置方式需要两个 @Configuration 配置类,而下面的方式,一个 @Configuration 配置类就可以了。
@ImportResource
导入配置文件注解。@Configuration 类也可以使用 @ImportResource 注解将 Spring XML 配置文件导入 @Configuration类。
@Configuration
@ImportResource("classpath:/com/acme/database-config.xml")
public class AppConfig {
@Inject DataSource dataSource; // from XML
@Bean
public MyBean myBean() {
// inject the XML-defined dataSource bean
return new MyBean(this.dataSource);
}
}
database-config.xml 配置文件中的内容就被导入到 AppConfig 当中了。
用途不是特别大,不过对于老系统的架构迁移来说,也是一种解决方案,比如从 Spring XML 架构迁移到 Spring Boot 架构。
嵌套
@Configuration 类可以相互嵌套。
@Configuration
public class AppConfig {
@Inject
DataSource dataSource;
@Bean
public MyBean myBean() {
return new MyBean(dataSource);
}
@Configuration
static class DatabaseConfig {
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder().build();
}
}
}
内层的 DataSource 也会自动注入到外层的 dataSource 当中。
在这种情况下,仅需要针对应用程序上下文注册 AppConfig。由于是嵌套的 @Configuration 类,因此将自动注册 DatabaseConfig。当 AppConfig 和 DatabaseConfig 之间的关系已经很明显的时候,就无需使用 @Import 注解了。
此外,嵌套的 @Configuration 类可以与 @Profile 注解一起搭配使用,为内层的 @Configuration 类提供同一配置 bean 的两个不同选择,比如开发环境和测试环境,相同 bean 的两种选择。
在一个 Spring Boot 的项目中,基本都会存在一个 config 的代码包。里面存放着项目的各种各样配置类,而配置的具体值,很可能放置在外部的 properties 或者 yml 文件中。
延迟加载
默认情况下,在容器启动的时候,被 @Bean 注解修饰的方法会提早实例化。为了避免这种情况,而是用到的时候才初始化。@Configuration 注解可以与 @Lazy 注解搭配使用,用来标识所有的被 @Bean 注解所修饰的方法,在默认情况下都是延迟初始化的。另外 @Lazy 也可以用于单个的 @Bean 方法搭配使用。也就是如果 @Lazy 修饰类,类中所有的 @Bean 方法都是延迟初始化,如果 @Lazy 修饰具体的一个 @Bean 方法,只有这个方法才是延迟初始化。
测试
Spring 测试模块中提供的 Spring TestContext 框架提供了 @ContextConfiguration 注解,它可以接受@Configuration 类对象的数组。
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {AppConfig.class, DatabaseConfig.class})
public class MyTests {
@Autowired MyBean myBean;
@Autowired DataSource dataSource;
@Test
public void test() {
// assertions against myBean ...
}
}
约束
@Configuration 所修饰的类是有几点约束。
@Configuration 必须修饰类。
@Configuration 类必须是非最终类,不能被 final 修饰。
@Configuration 类必须是非本地的(即不得在方法中声明)。
嵌套的 @Configuration 类必须声明为静态,被 static 修饰。
@Bean 方法可能不会创建更多的配置类(任何这样的实例都将被视为常规 Bean,其配置注释保持未被检测到)。
结尾
在 Spring Boot 的开发中,这种 @Configuration 类是一定会出现的。有些配置是使用了某些外部 jar 配置的,还有些配置是根据项目实际需要自定义的。自定义的配置一定要以 @Configuration 这种方式来应用。
Last updated
Was this helpful?