繁体   English   中英

Bean light 模式配置如何在 bean 上创建代理

[英]How Bean light mode configuration creates proxy on beans

我读到@Bean精简版模式春天文档的一部分, 在这里我的理解,如果配置被注释为组件然后春天不会创建代理类,这个配置和这个类里面的所有配置的bean被视为普通的方法调用。 但是,根据此示例,Spring 为 bean 创建了代理,注释为@Transactional并在@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

我会尽力解释

如果 config 被注释为组件,则 spring 不会创建此配置的代理类

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

因此,例如,如果使用@Transactional注释该类,则Conf类 bean 将成为代理。

此类中所有配置的 bean 都被视为普通方法调用

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

来自@Bean的文档

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

所以观察到

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

由于原因,正如预期的那样

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

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

显着的变化是

  1. 使用@Transactional注释@Component注释的类以解释代理。
  2. 添加@Configuration类来解释“豆间引用”支持
  3. 没有@Transactional for 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. 由于Lite模式, ComponentBean返回两个不同的 bean 实例。
  3. ConfigurationBean返回相同的实例。
  4. ConfigurationBean实例没有被代理。

@kriegaex 关于@Configuration类的工作有一个很好的答案 阅读。

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM