spring @Configuration源码详解

版权声明:原创文章,转载请明确标识来源 https://blog.csdn.net/ws008/article/details/79963991
概述
@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处理,不解析相关配置注解)


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页