It' well-known that @Transactional
or any other AOP around the method won't work if it was called from inside of the same class.
And explanation is clear and always made sense to me: the proxy wraps a real basic Java object. All the other clients of our class actually have a reference to proxy (injected by Spring), that's why AOP logic works. Inside of the same class we have a clean this
without AOP, hence AOP does not work if you call AOP-suggesting method directly on this
.
But at the same time there are @Configuration
classes with @Bean
methods for producing singletons which somehow get executed only once even if there is the code which explicitly invokes such method multiple times on this
. Here is the practical example:
@Bean
String bean1() {
System.out.println("Creating bean1 only once");
return new StringBuilder("bean1").toString();
}
@Bean
StringHolder bean2() {
return new StringHolder("bean2", bean1());
}
@Bean
StringHolder bean3() {
return new StringHolder("bean3", bean1());
}
Both bean2()
and bean3()
explicitly call bean1()
, but the "Creating bean1 only once" will be printed only once on context startup. How does it even work? The @Transactional
/AOP examples linked above teach us that the framework's magic should not work when we call self methods.
The behavior of @Bean
in @Configuration
on the other hand is expected according to what Spring Core documentation says:
CGLIB proxying is the means by which invoking methods or fields within
@Bean
methods in@Configuration
classes creates bean metadata references to collaborating objects. Such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans, even when referring to other beans through programmatic calls to@Bean
methods
I've decided to check in debugger what is the reference to this
inside of a @Bean
method of @Configuraiton
class and it turned out that this
is not just an instance of MyConfigClass
, but an instance of MyConfigClass$$SpringCGLIB$$0
.
So, this is a real evidence that this
can somehow point to a CGLIB proxy and not just a basic instance of original class.
If it can be achieved, why the same technique is not applied to instances of classes with @Transactional
and other AOP?
My first clue was that @Transactional
/AOP probably uses different types of proxies, so I've also inspected the reference to MyTransactionalService
. Inside of a method of the service this
points to instance of basic MyTransactionalService
, but outside of the class DI container will inject a reference to MyTransactionalService$$SpringCGLIB$$0
.
So, it looks like the same CGLIB approach is used for both cases: MyConfigClass
and MyTransactionalService
. But why methods of MyConfigClass
see proxied object behind this
ref, and at the same time methods of MyTransactionalService
see bare non-proxied object?