Spring proxies are based on the Proxy design pattern and serve as intermediaries to other resources, offering extra features at a slight performance penalty. For example, they facilitate lazy resource initialization and data caching.
The @Configuration annotation enables this mechanism by default through the proxyBeanMethods attribute set to
true. This ensures that the @Bean methods are proxied in order to enforce bean lifecycle behavior, e.g. to return shared
singleton bean instances even in case of direct @Bean method calls in user code. This functionality is achieved via method interception,
implemented through a runtime-generated CGLIB subclass.
When setting the proxyBeanMethods attribute to false the @Bean methods are not proxied and this is similar
to removing the @Configuration stereotype. In this scenario, @Bean methods within the @Configuration annotated
class operate in lite
mode, resulting in a new bean creation each time the method is invoked.
For Singleton beans, this could cause unexpected outcomes as the bean is created multiple times instead of being created once and
cached.
The rule raises an issue when the proxyBeanMethods attribute is set to false and the @Bean method of a
Singleton bean is directly invoked in the @Configuration annotated class code.
The issue can be fixed in the following ways:
@Bean method directly, but rather injecting the bean in the context and using it, by means of @Bean
method parameters.
proxyBeanMethods attribute, so that the @Bean
methods are proxied and the bean lifecycle is enforced. In the example below, every instance of PrototypeBean will have a different instance of SingletonBean, as
singletonBean() is called directly from prototypeBean().
@Configuration(proxyBeanMethods = false)
class ConfigurationExample {
@Bean
public SingletonBean singletonBean() {
return new SingletonBean();
}
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean(singletonBean()); // Noncompliant, the singletonBean is created every time a prototypeBean is created
}
class SingletonBean {
// ...
}
class PrototypeBean {
// ...
public PrototypeBean(SingletonBean singletonBean) {
// ...
}
// ...
}
}
The compliant solution relies on the @Bean method parameter to automatically inject the SingletonBean from the
ApplicationContext. This way every instance of PrototypeBean will have the same instance of SingletonBean.
@Configuration(proxyBeanMethods = false)
class ConfigurationExample {
@Bean
public SingletonBean singletonBean() {
return new SingletonBean();
}
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean(SingletonBean singletonBean) { // Compliant, the singletonBean is injected in the context and used by every prototypeBean
return new PrototypeBean(singletonBean);
}
class SingletonBean {
// ...
}
class PrototypeBean {
// ...
public PrototypeBean(SingletonBean singletonBean) {
// ...
}
// ...
}
}