当前位置: 首页 > 知识库问答 >
问题:

Spring5中lite@Bean方法的行为

陈奇希
2023-03-14

来自Spring 5文档

当@bean方法在未使用@configuration注释的类中声明时,它们被称为以'lite'模式处理。在@组件中,甚至在普通的旧类中声明的Bean方法将被视为“lite”,包含类的主要目的不同,而@Bean方法只是一种额外的好处。例如,服务组件可能通过每个适用的组件类上的附加@bean方法向容器公开管理视图。在这样的场景中,@bean方法是一种简单的通用工厂方法机制。

与full@configuration不同,lite@bean方法不能声明bean间依赖关系。相反,它们对包含组件的内部状态进行操作,并可选地对它们可能声明的参数进行操作。因此,这样的@bean方法不应该调用其他@bean方法;每个这样的方法实际上只是一个特定bean引用的工厂方法,没有任何特殊的运行时语义。这里的积极副作用是在运行时不需要应用CGLIB子类,因此在类设计方面没有限制(即包含类可能是最终类等)。

常规Spring组件中的@bean方法的处理方式不同于Spring@Configuration类中的对应方法。不同的是,@Component类没有用CGLIB增强以拦截方法和字段的调用。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

所以对我来说,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");
    }
}

编辑:

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

共有1个答案

宣望
2023-03-14

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

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

@Configuration
public MyConfiguration {

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

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }
}

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

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

 类似资料:
  • Spring 4:https://docs.Spring.io/autorepo/docs/Spring/4.2.4.release/javadoc-api/org/springframework/web/context/request/requestattributes.html#scope_global_session Spring 5:https://docs.Spring.io/autor

  • 完整@Configuration vs“lite”@Bean模式?在Spring文档中,他们提到了不带@Configuration注释类的@Bean注释方法。有谁能帮我得到一个完美的例子,它在哪里有用(在那种情况下)? https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-标准注释(概

  • 我知道从中调用带注释的方法,前面已经讨论过了。 但是我不明白为什么当bean被覆盖时它就不起作用了。 我有一个遗留类,我不能修改。它是一个配置,同时也是一个业务bean。这是一个简化版本: 现在,调用返回而不是。 当配置被删除并且测试上下文的配置被设置为时,一切都能正常工作(结果为)。 在测试中注册bean有什么不同?

  • 本文向大家介绍详解Spring中Bean的加载的方法,包括了详解Spring中Bean的加载的方法的使用技巧和注意事项,需要的朋友参考一下 之前写过bean的解析,这篇来讲讲bean的加载,加载要比bean的解析复杂些,从之前的例子开始. Spring中加载一个bean的方式: 来看看getBean(String name)方法源码, 该getBean(String name)方法位于Abstra

  • 我创建了spring boot 2.0演示应用程序,其中包含两个使用WebClient进行通信的应用程序。当我从WebClient的响应中使用Flux的block()方法时,它们经常停止通信。由于某些原因,我想使用列表而不是通量。 服务器端应用程序是这样的。它只返回Flux对象。 而客户端(或BFF端)应用程序是这样的。我从服务器获取Flux,并通过调用block()方法将其转换为List。 虽然

  • 问题内容: 我注意到一种奇怪的行为,我不确定这是设计使然还是我自己的误解。Spring将在以ComponentScan标记的@ Service,@ Component等注释的Bean中实现@Lookup方法,但不会在@Configuration类(Application.java)中定义的@Bean中实现这种方法。 这不是什么大问题,因为我可以从配置中删除@Bean定义,而直接对其类进行注释。但我