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 e.g: 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
- A class annotated with
@Transactional
needs proxying
- 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
- Annotating
@Component
annotated class with @Transactional
to explain the proxying.
- Adding a
@Configuration
class to explain the 'inter-bean references' support
- 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
ComponentConf
bean is proxied.
ComponentBean
returns two different bean instances due to Lite mode.
ConfigurationBean
returns the same instance.
ConfigurationBean
instances are not proxied.
There is an excellent answer by @kriegaex on the working of a @Configuration
class. Do read.
Hope this helps.