spring @Configuration源码详解

标签: java spring java config @Configuration spring boot
8人阅读 评论(0) 收藏 举报
分类:
概述
@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处理,不解析相关配置注解)


查看评论

Spring详解全套视频教程【案例+源代码】

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。本套课程讲解的非常清晰仔细
  • 2017年11月26日 20:52

Spring源码研究之@Configuration

本人的解读基于3.2.10版本,好吧确实有点老, 主要是当时读源码时参考的书就是这个版本的, 见谅. 本来打算直接进行SpringBoot启动源码的解读的, 但读到后面发现对注解配置相关的原理的认识...
  • lqzkcx3
  • lqzkcx3
  • 2017-10-31 23:10:51
  • 378

【hibernate源码解析】之Configuration

解析hibernate的源码,首先看的就是这个Configuration类。初学hibernate时,有这么一段小程序,用来获得sessionFactory的。请看下面这段代码: pri...
  • sunaichao2010
  • sunaichao2010
  • 2015-05-05 20:03:02
  • 485

SPRING源码分析(四)之@Configuration @Bean的方式配置bean

在微服务大趋势下Springboot,Springcloud成为更多公司新项目技术框架的选型 ,Springboot简洁的基本零配置,很重要的一点是将以前通过xml方式配置bean转换为 通过@Co...
  • jin5203344
  • jin5203344
  • 2017-11-21 14:44:31
  • 398

SpringBoot-自动配置源码解析

接着上一篇博客《 SpringBoot-快速搭建WEB工程》提出的需要分析的三个方面:我们来深入的探究SpringBoot是如何在没有一个配置文件的情况下为我们启动好一个完整的WEB工程的,首先我们从...
  • doegoo
  • doegoo
  • 2016-08-18 14:46:01
  • 8252

spring boot 源码解析13-@ConfigurationProperties是如何生效的

前言新的一年到了,在这里先祝大家新年快乐.我们在上一篇spring boot 源码解析12-servlet容器的建立 中 分析 ServerProperties时,发现其类上有@Configurati...
  • qq_26000415
  • qq_26000415
  • 2017-12-31 17:28:28
  • 4033

Spring Boot Externalized Configuration

Spring Boot allows you to externalize your configuration so you can work with the same application c...
  • kangkanglou
  • kangkanglou
  • 2017-07-10 19:29:08
  • 302

spring boot实战(第十篇)Spring boot Bean加载源码分析

前言 前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程 refresh 首先来看SpringApplication#run方法中...
  • liaokailin
  • liaokailin
  • 2015-10-13 23:30:30
  • 21548

Spring中Configuration的理解

基本用途从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigAp...
  • koflance
  • koflance
  • 2017-03-02 03:29:44
  • 4223
    个人资料
    等级:
    访问量: 820
    积分: 87
    排名: 169万+
    文章分类
    文章存档