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
@Beanmethods in@Configurationclasses 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@Beanmethods
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?
 
    