【发布时间】:2018-06-27 18:57:05
【问题描述】:
当@Bean 方法在未注解的类中声明时 使用@Configuration,它们被称为在 “精简”模式。在 @Component 甚至在普通中声明的 Bean 方法 旧班级将被视为“精简版”,具有不同的主要目的 包含类和 @Bean 方法只是一种奖励 那里。例如,服务组件可以将管理视图暴露给 容器通过每个适用的附加 @Bean 方法 组件类。在这种情况下,@Bean 方法很简单 通用工厂方法机制。
与完整的@Configuration 不同,精简的@Bean 方法不能声明 豆间依赖关系。相反,它们在其包含操作 组件的内部状态和可选的参数,它们可能 宣布。 因此这种@Bean 方法不应调用其他@Bean 方法;每个这样的方法实际上只是一个工厂方法 特定的 bean 引用,没有任何特殊的运行时语义。 这里的积极副作用是不需要 CGLIB 子类化 在运行时应用,因此在类方面没有限制 设计(即包含类可能仍然是最终的等)。
处理常规 Spring 组件中的 @Bean 方法 与 Spring @Configuration 中的对应项不同 班级。 不同之处在于@Component 类没有增强 CGLIB 拦截方法和字段的调用。 CGLIB 代理是在@Bean 中调用方法或字段的方法 @Configuration 类中的方法创建 bean 元数据引用 合作对象; 普通 Java 不会调用此类方法 语义,而是通过容器以提供 Spring bean 的常规生命周期管理和代理,即使在 通过对 @Bean 方法的编程调用来引用其他 bean。 在 相反,在普通的 @Bean 方法中调用方法或字段 @Component 类具有标准的 Java 语义,没有特殊的 CGLIB 处理或其他应用约束。
我原以为下面的代码会抛出一个异常/bean1.bean2 为 null 并且不会执行 init 方法。但是,下面的代码运行良好并打印:
Should never be invoked
Expected null but is ch.litebeans.Bean2@402bba4f
所以在我看来,lite bean 的行为与从 @Configuration 注释类构造的 bean 相同。有人能指出在哪种情况下不是这种情况吗?
.
package ch.litebeans;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Bean1 bean1 = ctx.getBean(Bean1.class);
System.out.println("Expected null but is " + bean1.getBean2());
}
}
package ch.litebeans;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"ch.litebeans"})
public class ApplicationConfig {}
package ch.litebeans;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class Factory1 {
@Bean
public Bean1 getBean1(Bean2 bean2){
return new Bean1(bean2);
}
}
package ch.litebeans;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class Factory2 {
@Bean(initMethod = "init")
public Bean2 getBean2(){
return new Bean2();
}
}
package ch.litebeans;
public class Bean1 {
private Bean2 bean2;
public Bean1(Bean2 bean2){
this.bean2 = bean2;
}
public Bean2 getBean2(){
return bean2;
}
}
package ch.litebeans;
public class Bean2 {
public void init(){
System.out.println("Should never be invoked");
}
}
编辑:
根据 Mike Hill 的解释,我添加了一个示例来展示差异:
public class BeanLiteRunner {
public static void main(String[] args) {
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyComponent.class,
MyConfiguration.class);
MyComponent.MyComponentBean1 componentBean1 = acac.getBean(MyComponent.MyComponentBean1.class);
MyComponent.MyComponentBean1 componentBean2 = acac.getBean(MyComponent.MyComponentBean1.class);
MyConfiguration.MyConfigurationBean1 configurationBean1 = acac.getBean(MyConfiguration
.MyConfigurationBean1.class);
MyConfiguration.MyConfigurationBean1 configurationBean2 = acac.getBean(MyConfiguration
.MyConfigurationBean1.class);
}
}
@Component
public class MyComponent {
@Bean
public MyComponent.MyComponentBean1 getMyComponentBean1(){
return new MyComponent.MyComponentBean1(getMyComponentBean2());
}
@Bean
public MyComponent.MyComponentBean2 getMyComponentBean2(){
return new MyComponent.MyComponentBean2();
}
public static class MyComponentBean1{
public MyComponentBean1(MyComponent.MyComponentBean2 myComponentBean2){
}
}
public static class MyComponentBean2{
public MyComponentBean2(){
System.out.println("Creating MyComponentBean2");
}
}
}
@Configuration
public class MyConfiguration {
@Bean
public MyConfigurationBean1 getMyConfigurationBean1(){
return new MyConfigurationBean1(getMyConfigrationBean2());
}
@Bean
public MyConfigurationBean2 getMyConfigrationBean2(){
return new MyConfigurationBean2();
}
public static class MyConfigurationBean1{
public MyConfigurationBean1(MyConfigurationBean2 myConfigurationBean2){}
}
public static class MyConfigurationBean2{
public MyConfigurationBean2(){
System.out.println("Creating MyConfigrationBean2");
}
}
}
输出符合预期
> Creating MyComponentBean2
> Creating MyComponentBean2
> Creating MyConfigrationBean2
【问题讨论】:
-
尝试将 getter
getBean2()重命名为Bean1类中的其他名称。 -
@NiVeR 谢谢,我刚试了一下,没什么区别。
-
@Pavel:这个问题在这里不适用,因为 OP 正在询问 Spring 5 中的一个新功能。
-
它在 Spring 3.0 中是否正常工作?这个功能是3.0版本开始的,可能是5.0版本的bug?