@Configuration
这是一个类级注解。如下所示,被它注解的类可能包含多个被@Bean
注解的方法。Spring容器会调用这些方法,获得你初始化后的对象实例,并把他们注册为容器内的beans。
package spring.example
@Configuration
public class MyAppConfig {
@bean
public SomeBean someBean() {
// 实例化并返回,也可进行初始化
return new SomeBeanImpl();
}
}
同等作用的XML配置会像下面这样:
@Configuration
类们实际上就是Spring管理的用于创建并注册bean实例的工厂。
Spring容器的启动
在Java-based的配置方式下,spring容器可以被AnnotationConfigApplicationContext
启动,或者,针对Web应用AnnotationConfigWebApplicationContext
也行。
new AnnotationConfigApplicationContext(MyAppConfig.class);
我们也可以指定包含了@Configuration
类的有效包名:
new AnnotationConfigApplicationContext("spring.example");
基于上述两个重载方法,我们可以在单个package下放进多个JavaConfig类。
使用多个JavaConfig类
new AnnotationConfigApplicationContext( AppConfig.class, DataSourceConfig.class );
new AnnotationConfigApplicationContext("example.spring.app","example.spring.datasource");
配置类中的依赖注入
既然配置类会被Spring容器注册成beans,那意味着,我们可以像使用普通bean那样使用这个配置bean。在以下例子我们要把这个一个配置bean注入给另一个配置bean:
@Configuration
public class AppConfig {
// 方式一:注入DataSourceConfig
@Autowired
private DataSourceConfig dataSourceConfig;
@Bean
Client clientBean() {
return new Client(dataSourceConfig.dataSourceBean());
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class, DataSourceConfig.class);
context.getBean(Client.class).showData();
}
// 方式二:为何要那么麻烦呢?直接注入DataSourceBean不就好了?
@Autowired
private DataSourceBean dataSourceBean;
@Bean
Client clientBean() {
return new Client(dataSourceBean);
}
}
@Configuration
class DataSourceConfig {
@Bean
DataSourceBean dataSourceBean() {
return new DataSourceBean();
}
}
class Client {
private DataSourceBean dataSourceBean;
Client(DataSourceBean dataSourceBean){
this.dataSourceBean = dataSourceBean;
}
public void showData() {
System.out.println(dataSourceBean.getData());
}
}
class DataSourceBean {
public String getData() {
return "some data";
}
}
在JavaConfig中使用构造器注入
从Spring 4.3开始,配置类开始支持使用构造器注入,可以从以下例子感受下何种场景下可以使用这个特性,see also:Spring - Implicit constructor Injection
@Configuration
@ComponentScan({"com.logicbig.example.service", "com.logicbig.example.client"})
public class ConfigurationImplicitConstructor {
private final OrderService orderService;
public ConfigurationImplicitConstructor (OrderService orderService) {
this.orderService = orderService;
}
@Bean(name = "services")
public List services(){
return Arrays.asList(orderService);
}
public static void main (String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
ConfigurationImplicitConstructor.class);
Object services = context.getBean("services");
System.out.println(services);
}
}
@Configuration类都会被CGLIB子类化
所有的@Configuration类都会在应用启动阶段被CGLIB进行子类化。在生成的子类当中,子方法会首先检查容器内已缓存的beans,如果已缓存的beans中没有当前实例,子方法才会真正去调用父方法,即是真正创建一个新的对象实例
在@Configuration类的@Bean方法中调用的[方法或字段],就是通过CGLIB代理,去对协作对象创建bean元数据引用。
这同时也是为何多次调用同一方法时,只会返回同一个实例的原因(因为默认的scoped为singleton)。@Configuration
注解是必须的,否则这个CGLIB代理不会被Spring执行。
运行以下代码,你可以看到输出结果是一致的。另外也可以把@Configuration
去掉后,再运行看看结果。
@Configuration
public class SpringConfig {
@Bean
public String something(){
return new String(System.nanoTime());
}
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println("Spring container started and is ready");
SpringConfig bean = context.getBean(SpringConfig.class);
System.out.println(bean.something());
System.out.println(bean.something());
}
}
请注意在上例中,SpringConfig是作为一个bean实例被我们获取到的。这也是@Configuration
类会被作为bean注册的一个有力证明。