简体   繁体   中英

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. However, according to this example, Spring created proxy for bean annotated as @Transactional and configured inside @Component class

@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. The question is, if configuration class is not a proxy then how Spring created a proxy for method public ProxyBean bean() ? Spring Boot version - 2.1.6

I will try to explain

if config is annotated as component then spring doesn't create proxy class of this config

Spring container creates proxy for a bean only if it is required , like for any special processing for the bean eg: AOP , Transaction Management. I have explained this for another SO question here , please go through A2 section of the answer if interested.

So for example , the Conf class bean will be a proxy if the class is annotated with @Transactional .

all configured beans inside this class are treated as plain method calls

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 . In a @Configuration annotated class , multiple calls to a @Bean annotated method returns the same bean instance.

From the documentation of @Bean

In contrast to the semantics for bean methods in @Configuration classes, 'inter-bean references' are not supported in lite mode. Instead, when one @Bean-method invokes another @Bean-method in lite mode, the invocation is a standard Java method invocation; Spring does not intercept the invocation via a CGLIB proxy. 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.

So the observation that

Spring created proxy for bean annotated as @Transactional and configured inside @Component class

is as expected for the reasons

  1. A class annotated with @Transactional needs proxying
  2. A class annotated with @Component does not require any special processing in this example

I have modified your example to explain this better

Notable changes are

  1. Annotating @Component annotated class with @Transactional to explain the proxying.
  2. Adding a @Configuration class to explain the 'inter-bean references' support
  3. No @Transactional for ConfigurationBean.init() method to explain the proxying.

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.
  2. ComponentBean returns two different bean instances due to Lite mode.
  3. ConfigurationBean returns the same instance.
  4. ConfigurationBean instances are not proxied.

There is an excellent answer by @kriegaex on the working of a @Configuration class. Do read.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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