spring @Configuration源码详解

概述
@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 >
< context :annotation-config />//启用 ConfigurationClassPostProcessor 等来处理@ @Configuration
< 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本身也是一个@Component,因此配置类本身也会被注册到应用上下文,并且也可以使用IOC的 @Autowired/ @Inject 等注解来注入所需bean。
我们来修改配置类如下:
@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处理,不解析相关配置注解)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值