一、介绍
从@Configuration的定义来看,@Configuration被@Component注解了,因此被@Configuration注解的类也会被加入到容器中,但是@Configuration配合@Bean还有其他的作用。下面看看官方文档介绍:
That said, there is a ‘lite’ mode of @Bean processing where we don’t apply any CGLIB processing: simply declare your @Bean methods on classes not annotated with @Configuration (but typically with another Spring stereotype instead, e.g. @Component). As long as you don’t do programmatic calls between your @Bean methods, this is going to work just as fine.
就是说@Bean在@Configuration中定义bean,会使用CGIB处理,因此可以@Bean方法中可以调用其他@bean方法,每次该bean方法产生的都是同一个Bean,维护他的singleton范围(Scope)。而Component不会,在@Bean方法中调用会产生多个Bean,尽管是singleton范围的。实际情况是这样的吗?。。。呵呵,当然不是。。。
二、实例代码
看看下面代码:
@Configuration
public static class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}
@Component
public static class Config {
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}
第一块代码如果运行正常,SimpleBeanConsumer会得到单实例SimpleBean,但是哦,用eclipse运行下,发现根本不是单实例SimpleBean。因为CGIB没有正常工作,稍后解释。
第二块代码中,Spring不能控制Bean的实例过程,只是接受方法的返回值,然后注册为bean而已。因此在simpleBeanConsumer方法中创建了另一个SimpleBean实例。
三、@Configuration中的@Bean方法执行的实际过程。
在@Configuration中,所有的被@Bean注解的方法都会包装成CGIB包装类,调用@Bean方法实际是调用了该包装类的方法。这个方法主要干了什么呢?下面是spring文档的原话:
All
@Configuration
classes are subclassed at startup-time withCGLIB
. In the subclass, the child method checks the container first for any cached (scoped) beans before it calls the parent method and creates a new instance
也就是说,执行原先的@Bean方法时先测查一下有没有缓存的bean,在singleton范围内,则会直接返回该Bean,因此不会出现两个singleton范围的Bean,维护了singleton的范围。但是由于某种原因,CGIB没有正常工作,因此测试中和@Component没啥区别。具体原因请看这篇文章:CGLIB: signer information does not match signer information of other classes,大致原因就是spring的jar包是有签名的,我们的代码在没有打包、签名、部署之前是没有签名的,因此默认类加载器在验证时就出错了。
四、@Component代码修正
第二块代码没有维护simpleBean单实例的范围,因为new SimpleBeanConsumer(simpleBean())就是被单纯的调用了一下,可以通过下面的代码修正:
@Component
public static class Config {
@Autowired
SimpleBean simpleBean;
@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean);
}
}
参考:
http://dimafeng.com/2015/08/29/spring-configuration_vs_component/
http://dimafeng.com/2015/08/16/cglib/