【问题标题】:How Bean light mode configuration creates proxy on beansBean 轻模式配置如何在 bean 上创建代理
【发布时间】:2020-03-22 13:05:06
【问题描述】:

我阅读了有关@Bean Lite 模式here 的spring doc 的一部分 据我了解,如果 config 被注释为组件,则 spring 不会创建此配置的代理类,并且此类内的所有配置 bean 都被视为普通方法调用。然而,根据这个例子,Spring 为注解为 @Transactional 的 bean 创建了代理,并在 @Component 类中配置

@SpringBootApplication
public class TranslatorApplication implements CommandLineRunner {

    @Autowired
    ProxyBean bean;

    @Autowired
    Conf conf;

    public static void main(final String[] args) {
        SpringApplication.run(TranslatorApplication.class, args);
    }

    @Override
    public final void run(final String... args) {
        System.out.println(conf.getClass().getSimpleName());
        System.out.println(bean.getClass().getSimpleName());
    }

    @Component
    static class Conf {
        @Bean
        public ProxyBean bean() {
            return new ProxyBean();
        }
    }

    static class ProxyBean {

        @Transactional
        public void init() {

        }
    }
}

输出:

Conf
TranslatorApplication$ProxyBean$$EnhancerBySpringCGLIB$$f4c1a493

这意味着ProxyBean是CGLIB创建的代理。 问题是,如果配置类不是代理,那么 Spring 如何为方法 public ProxyBean bean() 创建代理? Spring Boot 版本 - 2.1.6

【问题讨论】:

    标签: java spring spring-boot


    【解决方案1】:

    我会尽力解释

    如果配置被注释为组件,那么 spring 不会创建代理 这个配置的类

    Spring 容器仅在需要时为 bean 创建代理,例如对 bean 的任何特殊处理,例如:AOP、事务管理。我已经为另一个 SO 问题 here 解释了这一点,如果有兴趣,请查看答案的 A2 部分。

    因此,例如,Conf 类 bean 将是一个代理,如果该类使用 @Transactional 注释。

    这个类中所有配置的bean都被视为普通方法 来电

    不正确。 Lite 模式下的所有自调用或内部方法调用都是普通的方法调用,这与带有@Configuration 注释的类中的特殊处理 不同。在@Configuration 注释类中,多次调用@Bean 注释方法返回相同的bean 实例。

    来自@Bean的文档

    与 @Configuration 中 bean 方法的语义相反 精简模式不支持类,“bean 间引用”。 相反,当一个 @Bean-method 在 lite 中调用另一个 @Bean-method 模式,调用是标准的Java方法调用;春天确实 不通过 CGLIB 代理拦截调用。这类似于 inter-@Transactional 方法调用在代理模式下,Spring 没有 拦截调用——Spring 只在 AspectJ 模式下这样做。

    所以观察到

    Spring 为注解为 @Transactional 的 bean 创建了代理,并且 在@Component 类中配置

    原因如预期

    1. 使用@Transactional 注释的类需要代理
    2. @Component 注解的类在本例中不需要任何特殊处理

    我已经修改了您的示例以更好地解释这一点

    显着的变化是

    1. @Transactional 注释@Component 注释类以解释代理。
    2. 添加 @Configuration 类来解释“bean 间引用”支持
    3. 没有@Transactional 用于ConfigurationBean.init() 方法来解释代理。

    代码

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.annotation.Transactional;
    
    @SpringBootApplication
    public class TranslatorApplication implements CommandLineRunner {
    
        @Autowired
        ComponentBean beanOne;
    
        @Autowired
        ComponentBean beanTwo;
    
        @Autowired
        ComponentConf conf;
    
        @Autowired
        ConfigurationBean beanThree;
    
        @Autowired
        ConfigurationBean beanFour;
    
        @Autowired
        ConfigurationConf config;
    
        public static void main(final String[] args) {
            SpringApplication.run(TranslatorApplication.class, args);
        }
    
        @Override
        public final void run(final String... args) {
            System.out.println(conf+" : "+conf.getClass().getSimpleName());
            System.out.println(beanOne+" : "+beanOne.getClass().getSimpleName());
            System.out.println(beanTwo+" : "+beanTwo.getClass().getSimpleName());
            System.out.println(config+" : "+config.getClass().getSimpleName());
            System.out.println(beanThree+" : "+beanThree.getClass().getSimpleName());
            System.out.println(beanFour+" : "+ beanFour.getClass().getSimpleName());   
        }
    
        interface ComponentConfIntf{}
    
        @Component
        @Transactional
        static class ComponentConf{
            @Bean
            public ComponentBean beanOne() {
                return new ComponentBean();
            }
    
            @Bean
            public ComponentBean beanTwo() {
                return beanOne();
            }
        }
    
        static class ComponentBean {
    
            @Transactional
            public void init() {
    
            }
        }
    
        @Configuration
        static class ConfigurationConf {
            @Bean
            public ConfigurationBean beanThree() {
                return new ConfigurationBean();
            }
    
            @Bean
            public ConfigurationBean beanFour() {
                return beanThree();
            }
        }
    
        static class ConfigurationBean {
    
            public void init() {
    
            }
        }
    }
    

    打印

    rg.xx.xx.TranslatorApplication$ComponentConf@8a589a2 : TranslatorApplication$ComponentConf$$EnhancerBySpringCGLIB$$e204f764
    rg.xx.xx.TranslatorApplication$ComponentBean@c65a5ef : TranslatorApplication$ComponentBean$$EnhancerBySpringCGLIB$$d3d05c88
    rg.xx.xx.TranslatorApplication$ComponentBean@6b5176f2 : TranslatorApplication$ComponentBean$$EnhancerBySpringCGLIB$$d3d05c88
    rg.xx.xx.TranslatorApplication$ConfigurationConf$$EnhancerBySpringCGLIB$$9369a982@b672aa8 : TranslatorApplication$ConfigurationConf$$EnhancerBySpringCGLIB$$9369a982
    rg.xx.xx.TranslatorApplication$ConfigurationBean@2fab4aff : ConfigurationBean
    rg.xx.xx.TranslatorApplication$ConfigurationBean@2fab4aff : ConfigurationBean
    

    请注意

    1. ComponentConf bean 已被代理。
    2. ComponentBean 由于 Lite 模式返回两个不同的 bean 实例。
    3. ConfigurationBean 返回相同的实例。
    4. ConfigurationBean 实例未被代理。

    @kriegaex 有一个出色的 answer@Configuration 类的工作。请阅读。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 2018-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多