简体   繁体   English

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

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

I read a part of spring doc about @Bean Lite mode here As I understand, if config is annotated as component then spring doesn't create proxy class of this config and all configured beans inside this class are treated as plain method calls.我读到@Bean精简版模式春天文档的一部分, 在这里我的理解,如果配置被注释为组件然后春天不会创建代理类,这个配置和这个类里面的所有配置的bean被视为普通的方法调用。 However, according to this example, Spring created proxy for bean annotated as @Transactional and configured inside @Component class但是,根据此示例,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() {

        }
    }
}

The output:输出:

Conf
TranslatorApplication$ProxyBean$$EnhancerBySpringCGLIB$$f4c1a493

Which means that ProxyBean is a proxy created by CGLIB.这意味着ProxyBean 是CGLIB 创建的代理。 The question is, if configuration class is not a proxy then how Spring created a proxy for method public ProxyBean bean() ?问题是,如果配置类不是代理,那么 Spring 如何为public ProxyBean bean()方法创建代理? Spring Boot version - 2.1.6 Spring Boot 版本 - 2.1.6

I will try to explain我会尽力解释

if config is annotated as component then spring doesn't create proxy class of this config如果 config 被注释为组件,则 spring 不会创建此配置的代理类

Spring container creates proxy for a bean only if it is required , like for any special processing for the bean eg: AOP , Transaction Management. Spring 容器仅在需要时为 bean 创建代理,例如对 bean 的任何特殊处理,例如:AOP、事务管理。 I have explained this for another SO question here , please go through A2 section of the answer if interested.我已经在这里为另一个 SO 问题解释了这一点,如果有兴趣,请阅读答案的 A2 部分。

So for example , the Conf class bean will be a proxy if the class is annotated with @Transactional .因此,例如,如果使用@Transactional注释该类,则Conf类 bean 将成为代理。

all configured beans inside this class are treated as plain method calls此类中所有配置的 bean 都被视为普通方法调用

Not correct.不正确。 All the self invocation or internal method calls in a Lite mode are plain method calls in contrast to the special processing within a class annotated with @Configuration .与使用@Configuration注释的类中的特殊处理相比, Lite模式中的所有自调用或内部方法调用都是普通方法调用。 In a @Configuration annotated class , multiple calls to a @Bean annotated method returns the same bean instance.@Configuration注释类中,对@Bean注释方法的多次调用返回相同的 bean 实例。

From the documentation of @Bean来自@Bean的文档

In contrast to the semantics for bean methods in @Configuration classes, 'inter-bean references' are not supported in lite mode.与@Configuration 类中 bean 方法的语义相反,精简模式不支持“bean 间引用”。 Instead, when one @Bean-method invokes another @Bean-method in lite mode, the invocation is a standard Java method invocation;相反,当一个@Bean-method 在 lite 模式下调用另一个 @Bean-method 时,该调用是标准的 Java 方法调用; Spring does not intercept the invocation via a CGLIB proxy. Spring 不会通过 CGLIB 代理拦截调用。 This is analogous to inter-@Transactional method calls where in proxy mode, Spring does not intercept the invocation — Spring does so only in AspectJ mode.这类似于 @Transactional 间的方法调用,在代理模式下,Spring 不会拦截调用——Spring 只在 AspectJ 模式下这样做。

So the observation that所以观察到

Spring created proxy for bean annotated as @Transactional and configured inside @Component class Spring 为 bean 创建了代理,注释为 @Transactional 并在 @Component 类中配置

is as expected for the reasons由于原因,正如预期的那样

  1. A class annotated with @Transactional needs proxying@Transactional注释的类需要代理
  2. A class annotated with @Component does not require any special processing in this example@Component注解的@Component在本例中不需要任何特殊处理

I have modified your example to explain this better我已经修改了您的示例以更好地解释这一点

Notable changes are显着的变化是

  1. Annotating @Component annotated class with @Transactional to explain the proxying.使用@Transactional注释@Component注释的类以解释代理。
  2. Adding a @Configuration class to explain the 'inter-bean references' support添加@Configuration类来解释“豆间引用”支持
  3. No @Transactional for ConfigurationBean.init() method to explain the proxying.没有@Transactional for ConfigurationBean.init()方法来解释代理。

Code代码

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() {

        }
    }
}

Prints印刷

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

Do note that请注意

  1. ComponentConf bean is proxied. ComponentConf bean 被代理。
  2. ComponentBean returns two different bean instances due to Lite mode.由于Lite模式, ComponentBean返回两个不同的 bean 实例。
  3. ConfigurationBean returns the same instance. ConfigurationBean返回相同的实例。
  4. ConfigurationBean instances are not proxied. ConfigurationBean实例没有被代理。

There is an excellent answer by @kriegaex on the working of a @Configuration class. @kriegaex 关于@Configuration类的工作有一个很好的答案 Do read.阅读。

Hope this helps.希望这可以帮助。

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

相关问题 当Managed Bean创建并且bean的属性创建时? - When Managed Bean creates and property of the beans creates? Spring Bean配置:如何将Bean标记为必需/可选? - Spring Bean Configuration: How to mark beans as mandatory/optional? Spring创建了两个@Configuration bean启动 - Spring creates two of @Configuration beans starting up 在@Configuration bean 中的 SpEL 表达式中引用 ConfigurationProperties Beans - Referring to ConfigurationProperties Beans in SpEL expression in @Configuration bean 如何使用Spring XML配置来设置包含特定类型的所有bean的列表的bean属性? - How can I use Spring XML configuration to set a bean property with list of all beans of a certain type? 如何在调试和发布模式下从不同位置加载Spring bean xml配置 - How to load Spring beans xml configuration from different location in Debug and Release mode 使用beans.xml文件的CDI bean配置 - CDI bean configuration using beans.xml file 配置问题:Bean名称“ dataSource”已在此使用 <beans> 元件 - Configuration issue : Bean name “dataSource” is already used in this <beans> element Spring JavaConfig:通过“直接”调用Configuration类/ bean来检索bean - Spring JavaConfig: Retrieving beans by calling Configuration class/bean “directly” 如何以 bean 的形式访问配置? - How to access configuration as a bean?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM