概述
@Configuration注解提供了全新的bean创建方式。最初spring通过xml配置文件初始化bean并完成依赖注入工作。从spring3.0开始,在spring framework模块中提供了这个注解,搭配@Bean等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作。例如:
public interface
IBean {
}
public class
AppBean
implements
IBean{
}
@Configuration //@Configuration申明了
AppConfig
是一个配置类
public class
AppConfig {
@Bean //@Bean注解申明了一个bean,bean名称默认为方法名
appBean
IBean appBean(){
return new
AppBean();
}
}
Annotation解析
AnnotationConfigApplicationContext或者基于web的AnnotationConfigWebApplicationContext用来处理基于@Configuration注解的Bean初始化方式。
1、通过register方法处理@Configuration配置类
AnnotationConfigApplicationContext context =
new
AnnotationConfigApplicationContext();
context.register(AppConfig.
class
);
context.refresh();
IBean appBean = (IBean)context.getBean(
"appBean"
);
2、通过scan方法处理@Configuration配置类
AnnotationConfigApplicationContext context =
new
AnnotationConfigApplicationContext();
//包含配置类的包路径,支持多个包路径
context.scan(
"com.zhaolin81.spring.framework.config.annotation.appconfig"
);
context.refresh();
IBean appBean = (IBean)context.getBean(
"appBean"
);
XML <bean>标签解析
在类路径下创建spring config xml文件
config_bean.xml
<
beans
>
<
bean
class
="com.zhaolin81.spring.framework.config.annotation.appconfig.AppConfig"
/>
</
beans
>
通过ClassPathXmlApplicationContext加载xml配置文件
ClassPathXmlApplicationContext context =
new
ClassPathXmlApplicationContext(
"config_bean.xml"
);
context.refresh();
IBean appBean = (IBean)context.getBean(
"appBean"
);
@ComponentScan注解解析
是时候看一下@Configuration的源码了。
@Target
(ElementType.
TYPE
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Documented
@Component //@Component元注解
public
@
interface
Configuration
{
String value()
default
""
;
}
我们看到源码里面,@Configuration 标记了@Component元注解,因此可以被@ComponentScan扫描并处理(也可以使用XML
<context:component-scan/>)。
在类路径下创建spring config xml文件
componentscan_config_bean.xml
<
beans
>
<
context
:component-scan
base-package
="com.zhaolin81.spring.framework.config.annotation.appconfig"
/>
</
beans
>
通过ClassPathXmlApplicationContext加载xml配置文件
ClassPathXmlApplicationContext context =
new
ClassPathXmlApplicationContext(
"componentscan_config_bean.xml"
);
context.refresh();
IBean appBean = (IBean)context.getBean(
"appBean"
);
我们来修改配置类如下:
@Configuration
public class
AppConfig {
@Autowired
public
Environment
env
;
@Bean
IBean appBean(){
return new
AppBean();
}
}
然后执行如下代码:
ClassPathXmlApplicationContext context =
new
ClassPathXmlApplicationContext(
"componentscan_config_bean.xml"
);
context.refresh();
AppConfig appConfig = context.getBean(AppConfig.
class
);//直接获取配置类Bean
System
.
out
.println(appConfig.
env
.getProperty(
"file.encoding"
));//可以看到注入了成员
配置类也可以自己添加注解@CompomentScan,来显式扫描需使用组件。
@Configuration
@ComponentScan
(
"abc.xxx"
)
public class
AppConfig {
@Bean
IBean appBean(){
return new
AppBean();
}
}
使用外部数据
在配置类中,可以通过注入环境和配置相关参数对象,之后再构建Bean时,可以使用这些外部数据。
我们扩展一下AppBean:
public interface
IBean {
void
setName(String name);
String getName();
}
public class
AppBean
implements
IBean {
private
String
name
;
@Override
public void
setName(String name){
this
.
name
= name;
}
@Override
public
String getName(){
return
name
;
}
}
然后在配置类中,从环境变量获取数据并传递到Bean中
@Configuration
public class
AppConfig {
@Autowired
public
Environment
env
;
@Bean
IBean appBean(){
AppBean appBean =
new
AppBean();
appBean.setName(
env
.getProperty(
"xxx"
));//此处xxx需要替换成环境中存在的值
return
appBean;
}
}
最后执行如下代码
ClassPathXmlApplicationContext context =
new
ClassPathXmlApplicationContext(
"componentscan_config_bean.xml"
);
context.refresh();
IBean appBean = context.getBean(IBean.
class
);
System.
out
.println(appBean.getName());
也可以直接使用@Value注解来注入外部数据,这里不再赘述。
@Configuration组合使用
同@Import注解组合使用
新建一个配置类,例如数据库配置类:
@Configuration
public class
DatabaseConfig {
@Bean
public
DataSource dataSource(){
return new
DataSource(){...};
}
}
然后在AppConfig中用@Import来导入配置类
@Configuration
@Import
(DatabaseConfig.
class
)
public class
AppConfig {
@Autowired
public
DataSource
dataSource
; //注入的bean在DatabaseConfig.
class
中定义
@Bean
IBean appBean(){
return new
AppBean();
}
}
最后执行:
AnnotationConfigApplicationContext context =
new
AnnotationConfigApplicationContext();
context.register(AppConfig.
class
);
context.refresh();
DatabaseConfig dataSourceConfig = context.getBean(DatabaseConfig.
class
);
可以看到只注册了AppConfig.class,容器自动会把@Import指向的配置初始化。
同@Profile注解组合使用
在配置类中可以申明@Profile注解,仅当满足profile条件时,才会处理配置类,也可以将@Profile注解加载配置类中的每一个@Bean来实现更细粒度的条件控制。
@Configuration
@Profile
(
"develop"
)
public class
DatabaseConfig {
@Bean
public
DataSource dataSource(){
return new
DataSource(){...};
}
}
同@ImportReource注解组合使用
新建spring xml配置文件
config_other_bean.xml
<
beans
>
<
bean
class
="com.zhaolin81.spring.framework.config.annotation.otherconfig.DatabaseConfig"
></
bean
>
</
beans
>
修改配置类
@Configuration
@ImportResource
(
"classpath:
config_other_bean.xml
"
)
public class
AppConfig {
@Autowired
public
DataSource
dataSource
; //注入的bean在
config_other_bean.xml
中定义
@Bean
IBean appBean(){
return new
AppBean();
}
}
嵌套使用@Configuration
在配置类中可以创建静态内部类,并添加@Configuration注解,这样上下文只需要注册最外面的配置类,内部的配置类会自动被加载。这样做省略了@Import,因为本身就在配置类内部,无需再特别指定了。
@Configuration
public class
AppConfig {
@Autowired
public
DataSource
dataSource
; //注入的bean在内部定义
@Configuration
public static class
DatabaseConfig{
@Bean
DataSource dataSource(){
return new
DataSource() {...};
}
}
@Bean
IBean appBean(){
return new
AppBean();
}
}
@Lazy初始化
默认情况下,配置类中的Bean都随着应用上下文被初始化,可以在配置类中添加@Lazy注解来延迟初始化,当然也可以在每个@Bean注解上添加,来实现更细粒度的控制。
@Configuration
@Lazy//延时加载
public class
AppConfig {
@Bean
IBean appBean(){
return new
AppBean();
}
}
Spring测试
spring-test模块提供了
@ContextConfiguration注解来让我们方便的测试配置类,支持配置类数组。
@RunWith
(SpringJUnit4ClassRunner.
class
)
@ContextConfiguration
(classes={AppConfig.
class
, DatabaseConfig.
class
})
public class
Test {
@Autowired
IBean
bean
;
@Autowired
DataSource
dataSource
;
@org.junit.Test
public void
test(){
Assert.
assertNotNull
(
bean
);
Assert.
assertNotNull
(
dataSource
);
}
}
配置类约束
- 配置类必须为显式申明的类,而不能通过工厂类方法返回实例。允许运行时类增强。
- 配置类不允许标记final。
- 配置类必须全局可见(不允许定义在方法本地内部类中)
- 嵌套配置类必须申明为static 内部类
- @Bean方法不可以再创建新的配置类(所有实例都当做bean处理,不解析相关配置注解)