Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to set proxyBeanMethods to false in Springs @Configuration?

When looking at springs autoconfigure source code it seems that every auto-configuration class sets proxyBeanMethods = false.

@Configuration(proxyBeanMethods=false)
public class SomeAutoConfiguration {
    ...
}

The javadoc gives a detailed explanation for this particular field:

Specify whether {@code @Bean} methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct {@code @Bean} method calls in user code. (...) If this is not needed since each of this particular configuration's {@code @Bean} methods is self-contained and designed as a plain factory method for container use, switch this flag to {@code false} in order to avoid CGLIB subclass processing.(...)

After reading this I'm still confused when it is better setting it to false.

Here are my questions:

  • Can someone give a concrete example when this field should be true and explain why?
  • Why is this field set to false on the auto-configuration classes?

Update: Found two issues on github that give some explanation why it is false on most auto-configutation classes:

  • https://github.com/spring-projects/spring-boot/issues/9068
  • https://github.com/spring-projects/spring-framework/issues/22461
like image 479
jsattler Avatar asked Apr 17 '20 08:04

jsattler


People also ask

What is @configuration when to use it?

One of the most important annotations in spring is @Configuration annotation which indicates that the class has @Bean definition methods. So Spring container can process the class and generate Spring Beans to be used in the application. This annotation is part of the spring core framework.

What is proxyBeanMethods?

proxyBeanMethods allows you to invoke one method marked as Bean from another one declared in the same configuration class.

Does @configuration create a bean?

It is a method-level annotation. During Java configuration ( @Configuration ), the method is executed and its return value is registered as a bean within a BeanFactory .

How a Cglib proxy works with bean?

This is because Spring creates a CGLIB proxy around the @Configuration classes. The calls are intercepted and then Spring checks the container before creating a new bean for us. So to reiterate, when we invoke postRepository() three times, only the first invocation creates a Spring bean.


1 Answers

Something like this:

@Configuration(proxyBeanMethods=true)
public class SomeConfiguration {
    @Bean
    ServiceA serviceA(){
      return new ServiceA(sharedService());
    }

    @Bean
    ServiceB serviceB(){
      return new ServiceB(sharedService());
    }

    @Bean
    ServiceC sharedService(){
      return new ServiceC();
    }
}

Here, the proxyBeanMethods will assure that the 'sharedService' method will be intercepted and its result re-used. If you would follow the normal java logic, when calling serviceA() and serviceB(), there would be two different instances of ServiceC, and when calling sharedService() directly, a third instance would be created. Then proxy interceptor will make sure the actual method is only called once, so only one instance of the shared ServiceC is created, and both ServiceA and ServiceB will get the shared instance.

However proxyBeanMethods=true has a performance cost during startup, especially for libraries with a lot of @ Configuration classes, like spring-boot's internal libraries. See e.g. https://github.com/spring-projects/spring-boot/issues/9068#issuecomment-461520814 for the impact on Spring WebFlux. They couldn't change it to false by default, since that would break backwards compatibility. See links in the original question.

You can use different configuration patterns to avoid this, which is probably what auto-configuration classes do.

One way to do this is auto-wiring the service via method parameters instead of nested method calls. It makes less sence in normal Java, but it works in Spring configurations:

@Configuration(proxyBeanMethods=false)
public class SomeSmarterConfiguration {

    @Bean
    ServiceC sharedService(){
      return new ServiceC();
    }

    @Bean
    ServiceA serviceA(ServiceC sharedService){
      return new ServiceA(sharedService);
    }

    @Bean
    ServiceB serviceB(ServiceC sharedService){
      return new ServiceB(sharedService);
    }
}
like image 86
GeertPt Avatar answered Nov 15 '22 18:11

GeertPt