Start off with the latest spring-petclinic project based on Boot 2.3.x
Launch the app and see the JSON response for /actuator/beans
Search for OwnerController
bean. This bean should depend on OwnerRepository
and PetRepository
. The JSON for the bean however is this:
"ownerController": {
"aliases": [],
"scope": "singleton",
"type": "org.springframework.samples.petclinic.owner.OwnerController",
"resource": "file [/Users/aboyko/Documents/runtime-sts4/spring-petclinic/target/classes/org/springframework/samples/petclinic/owner/OwnerController.class]",
"dependencies": []
},
If spring-petclinic is switched to Boot 2.2.x then the dependencies are detected correctly:
"ownerController": {
"aliases": [],
"scope": "singleton",
"type": "org.springframework.samples.petclinic.owner.OwnerController",
"resource": "file [/Users/aboyko/Documents/runtime-sts4/spring-petclinic/target/classes/org/springframework/samples/petclinic/owner/OwnerController.class]",
"dependencies": [
"ownerRepository",
"visitRepository"
]
},
Something is off in the framework as actuator simply calls org.springframework.beans.factory.config.ConfigurableBeanFactory.getDependenciesForBean(String)
Try this in the org.springframework.samples.petclinic.PetClinicApplication
for the main method:
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(PetClinicApplication.class, args);
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
final Runnable printDependnecies = new Runnable() {
public void run() {
String[] dependenciesForBean = context.getBeanFactory().getDependenciesForBean("ownerController");
if (dependenciesForBean.length == 0) {
System.out.println("no dependencies!!!");
} else {
System.out.println("Dependencies:");
for (String dep : dependenciesForBean) {
System.out.println(dep);
}
System.out.println();
}
}
};
scheduler.scheduleAtFixedRate(printDependnecies, 10, 10, TimeUnit.SECONDS);
}
You'd see that there are no dependencies returned in the Boot 2.3.x case.
Comment From: snicoll
Spring Boot 2.3 uses the same Spring Framework generation as Spring Boot 2.2. If you've debugged this, it would be nice to share the two Spring Framework versions that you've used.
Comment From: BoykoAlex
Yes, it seems to be Spring 5.2.8.RELEASE in both cases. Should this be routed to Boot then?
Comment From: snicoll
Thank you. I've reproduced the problem and it does not occur with Spring Boot 2.2.9.RELEASE
indeed.
Comment From: snicoll
It's interesting to see that the bean dependencies aren't available in the core BeanRegistry
so I don't immediately see why it works with Spring Boot 2.2.9.RELEASE
but does not with 2.3.2.RELEASE
, given the same framework version is used.
Comment From: wilkinsona
I think this is a Framework bug. The change in behaviour is due to the JPA repositories being lazy by default in Spring Boot 2.3. The repository dependencies are detected again if you set spring.data.jpa.repositories.bootstrap-mode=default
. They're missing in the lazy case due to this logic in DefaultListableBeanFactory
:
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
In the lazy case, result
is non-null so autowiredBeanNames
is never populated.
Comment From: snicoll
Very nice detective work @wilkinsona!
@BoykoAlex you can workaround the problem in the meantime adding the following to application.properties
:
spring.data.jpa.repositories.bootstrap-mode=default
Comment From: snicoll
@jhoeller I've tentatively assigned 5.2.9
.
Comment From: jhoeller
This is more or less by design: Lazy proxy resolution doesn't know the actual targets upfront, and when it's eventually triggered, we cannot populate the original autowiredBeanNames
mechanism anymore. We'd have to somehow mimic this in a separate code path within the lazy resolution proxy. I'll investigate this as an enhancement, possibly in 5.3 only though.
Comment From: jhoeller
I got an implementation that's pretty straightforward, can easily make 5.2.9...
Comment From: jhoeller
And since this is somewhat unintuitive indeed when switching to a lazy dependency proxy, I'll consider it a bug fix to backport...
Comment From: martinlippert
Did this change make it into the 5.2.10 release? I am trying a pet clinic at Spring Boot 2.3.5 with Framework at 5.2.10 and the bean dependencies for the OwnerController (in our example above) are still empty. Will dive deeper tomorrow, but just wondering whether this fix made it into that version or not.
Comment From: jhoeller
@martinlippert see the milestone above - it made it into 5.2.9 and got backported all the way down to 4.3.29. Not sure why it's not working for PetClinic with Boot 2.3.5 still. The fix here was only really addressing the core @Lazy
problem, maybe some other difference between Boot 2.2 and 2.3 is involved as well?
Comment From: BoykoAlex
I'm still seeing the same JSON for the ownerController
bean with no dependencies and the main
method snippet prints no bean dependencies (issue description)... Boot 2.3.5 and Spring 5.2.10. The workaround spring.data.jpa.repositories.bootstrap-mode=default
still makes it work.
Seems like we may need to transfer this to boot now?
Comment From: jhoeller
Please note that dependencies behind a lazy proxy are only going to be registered once the proxy is actually being initialized, that is, once a method invocation on the proxy led to the resolution of the target instance. That's the part that the fix addressed here.
Comment From: BoykoAlex
@jhoeller Thanks for the last comment that clarified it really :-) Seems to all work as expected now. As soon as I add a new pet owner i see the clinic repo dependency. Think this is great :-)