春季5中lite @Bean方法的行为

Uel*_*ter 5 java spring

Spring 5文档

如果在未使用@Configuration注释的类中声明@Bean方法,则将它们称为在“精简”模式下进行处理。在@Component或什至在简单的旧类中声明的Bean方法将被视为“精简版”,其包含类的主要目的有所不同,而@Bean方法只是其中的一种奖励。例如,服务组件可以通过每个适用组件类上的其他@Bean方法将管理视图公开给容器。在这种情况下,@ Bean方法是一种简单的通用工厂方法机制。

与完整的@Configuration不同,lite @Bean方法无法声明Bean间的依赖关系。相反,它们对包含组件的内部状态以及可能声明的自变量进行操作。因此,这样的@Bean方法不应调用其他@Bean方法。每个这样的方法实际上只是用于特定bean引用的工厂方法,而没有任何特殊的运行时语义。这里的积极副作用是,不必在运行时应用CGLIB子类,因此在类设计方面没有任何限制(即,包含类仍然可以是最终类,等等)。

常规Spring组件中的@Bean方法的处理方式与Spring @Configuration类中的@Bean方法不同。不同之处在于,使用CGLIB不能增强@Component类,以拦截方法和字段的调用。CGLIB代理是一种调用@Configuration类中@Bean方法中的方法或字段的方法,以创建Bean元数据引用来协作对象。这样的方法不是用普通的Java语义调用的,而是经过容器以提供通常的生命周期管理和Spring bean的代理,即使通过@Bean方法的编程调用引用其他bean时也是如此。 相反,在普通@Component类内的@Bean方法中调用方法或字段具有标准Java语义,而无需特殊的CGLIB处理或其他约束。

我希望下面的代码抛出异常/ bean1.bean2为null,并且不会执行init方法。但是,以下代码可以正常运行并显示:

Should never be invoked
Expected null but is ch.litebeans.Bean2@402bba4f
Run Code Online (Sandbox Code Playgroud)

因此,对我而言,精简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");
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

基于对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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出符合预期

> Creating MyComponentBean2 
> Creating MyComponentBean2 
> Creating MyConfigrationBean2
Run Code Online (Sandbox Code Playgroud)

Mik*_*ill 18

这不是一个错误。在组件扫描期间,Spring 的 lite bean 定义会自动添加到上下文中。使用工厂方法的 Bean 定义(即所有@Bean定义的 Bean)将自动尝试使用当前上下文自动装配参数。有关ConstructorResolver#instantiateUsingFactoryMethod更多详细信息,请参阅。

引用的文档可能并不完全清楚。“精简”和“完整”bean 配置之间的主要区别严格来说是在“完整”( @Configuration) 模式下完成的代理。举个例子,如果我们在一个@Configuration类中定义我们的 bean 会发生什么:

@Configuration
public MyConfiguration {

    @Bean
    public Bean1 bean1() {
        return new Bean1(bean2());
    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }
}
Run Code Online (Sandbox Code Playgroud)

bean2()从内部调用bean1()实际上将引用上下文中的单例 bean2实例,而不是直接调用已bean2()实现的方法。事实上,bean2()从那个配置类中调用多少方法并不重要——真正的bean2方法只会被执行一次。

“Lite”模式不代理这些方法,这意味着每个bean2()方法调用实际上将直接引用方法实现,并且不会返回上下文中的 bean。相反,它将创建一个不受上下文跟踪的新的单独实例(这是默认的 Java 行为)。