Michel Jung opened SPR-15911 and commented

Posted in https://stackoverflow.com/questions/45955727/autowired-field-is-null-in-cglib-enhanced-bean

I have this repository:

@Repository
public class MyRepository {

  private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

  public MyRepository(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
    this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
  }

  void doSomething() {
    namedParameterJdbcTemplate.update(...);
  }
}

With Spring 4.3.7 everything worked as expected. But it no longer does since Spring 4.3.8:

When the bean is being instantiated, constructor-injection works as expected. However, when doSomething() is called, the debugger shows that my repository is an instance of MyRepository$$EnhancerBySpringCGLIB (wasn't with Spring 4.3.7) and namedParameterJdbcTemplate is null.

If I use @Service instead of @Repository the bean does not get CGLIB enhanced and everything works as expected.

I guess there's a reason why repositories are now GCLIB enhanced, but why doesn't auto wiring work anymore? I tried removing final but it didn't change anything.


Affects: 4.3.8

Attachments: - spr-15911-v4.3.7.log (4.52 MB) - spr-15911-v4.3.8.log (4.50 MB)

Comment From: spring-projects-issues

Juergen Hoeller commented

It looks like your call ends up in the CGLIB proxy instance (where all fields are null by design) rather than the actual target object (which has dependency injection applied). This usually only happens for methods which can't be intercepted such as final methods, since for a regular CGLIB proxy method, any such method will be overridden in order to forward the call through the interceptor chain, eventually ending up in the target instance.

I'm not aware of a regression in that area in 4.3.8 yet. Further insight would be much appreciated!

Comment From: spring-projects-issues

Michel Jung commented

Comparing debug logs of 4.3.7 and 4.3.8 with the exact same code revealed something.

This line is printed in 4.3.8 but not in 4.3.7:

o.s.aop.framework.CglibAopProxy          : Method [void com.faforever.server.chat.NickCoreRepository.updatePassword(java.lang.String,java.lang.String)] is package-visible across different ClassLoaders and cannot get proxied via CGLIB: Declare this method as public or protected if you need to support invocations through the proxy.

(I updated the issue to reflect the fact that my method wasn't public :-))

So I made the method public (against IntelliJ's recommendation to make it package-private ;-)) and the problem went away.

However, I've not been able to reproduce this in a minimalistic example, so there must be more to this story. The real-world project also uses caching, security, integration, actuator, jolokia If desired I can provide the source code (it's open source) and steps how to run it.

I also attached the log files, the actual class name to look for is NickCoreRepository and its bean NickCoreRepository.

I leave it up to you if you want to close this issue as invalid, or if you see a problem with this behavior. Keep in mind that this limitation didn't (seem to) exist pre-4.3.8 and it still does not with @Service :-)

Comment From: spring-projects-issues

Juergen Hoeller commented

Package-visible methods can only be intercepted and propagated if the original class and the generated proxy class live in the same class loader. We only really revised the log entry in 4.3.8, so I wonder why the actual behavior ends up being different... For a start, you could set a breakpoint in CglibAopProxy.doValidateClass and compare the class loader references that trigger the log entry there; I wonder why you have different class loaders there to begin with.

As for @Repository vs @Service, if you have a PersistenceExceptionTranslationPostProcessor registered, @Repository beans get proxied for exception translation purposes while @Service and other beans remain unproxied. From that perspective, it's no surprise that you're only seeing the effect for @Repository beans here.

Comment From: desiderati

The problem also happens if you use final methods!

Comment From: snicoll

Duplicate of #30938