[英]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由于原因,正如预期的那样
@Transactional
needs proxying用@Transactional
注释的类需要代理@Component
does not require any special processing in this example用@Component
注解的@Component
在本例中不需要任何特殊处理I have modified your example to explain this better我已经修改了您的示例以更好地解释这一点
Notable changes are显着的变化是
@Component
annotated class with @Transactional
to explain the proxying.使用@Transactional
注释@Component
注释的类以解释代理。@Configuration
class to explain the 'inter-bean references' support添加@Configuration
类来解释“豆间引用”支持@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请注意
ComponentConf
bean is proxied. ComponentConf
bean 被代理。ComponentBean
returns two different bean instances due to Lite mode.由于Lite模式, ComponentBean
返回两个不同的 bean 实例。ConfigurationBean
returns the same instance. ConfigurationBean
返回相同的实例。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.