Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy dependency proxy does not populate bean dependencies #25562

Closed
BoykoAlex opened this issue Aug 7, 2020 · 15 comments
Closed

Lazy dependency proxy does not populate bean dependencies #25562

BoykoAlex opened this issue Aug 7, 2020 · 15 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@BoykoAlex
Copy link

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.

@snicoll
Copy link
Member

snicoll commented Aug 7, 2020

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.

@BoykoAlex
Copy link
Author

Yes, it seems to be Spring 5.2.8.RELEASE in both cases. Should this be routed to Boot then?

@snicoll snicoll transferred this issue from spring-projects/spring-framework Aug 7, 2020
@snicoll snicoll changed the title ConfigurableBeanFactory.getDependenciesForBean(String) does not list beans injected via contructor arguments BeansEndpoint no longer exposes bean dependencies Aug 7, 2020
@snicoll
Copy link
Member

snicoll commented Aug 7, 2020

Thank you. I've reproduced the problem and it does not occur with Spring Boot 2.2.9.RELEASE indeed.

@snicoll
Copy link
Member

snicoll commented Aug 7, 2020

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.

@wilkinsona
Copy link
Member

wilkinsona commented Aug 7, 2020

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.

@snicoll
Copy link
Member

snicoll commented Aug 7, 2020

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

@snicoll snicoll changed the title BeansEndpoint no longer exposes bean dependencies LazyResolution does not populate bean dependencies Aug 7, 2020
@snicoll snicoll transferred this issue from spring-projects/spring-boot Aug 7, 2020
@snicoll snicoll added the type: bug A general bug label Aug 7, 2020
@snicoll snicoll modified the milestones: 5.x Backlog, 5.2.9 Aug 7, 2020
@snicoll
Copy link
Member

snicoll commented Aug 7, 2020

@jhoeller I've tentatively assigned 5.2.9.

@jhoeller jhoeller self-assigned this Aug 7, 2020
@jhoeller jhoeller added the in: core Issues in core modules (aop, beans, core, context, expression) label Aug 7, 2020
@jhoeller
Copy link
Contributor

jhoeller commented Aug 7, 2020

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.

@jhoeller jhoeller added type: enhancement A general enhancement and removed type: bug A general bug labels Aug 7, 2020
@jhoeller jhoeller changed the title LazyResolution does not populate bean dependencies Lazy dependency proxy does not populate bean dependencies Aug 7, 2020
@jhoeller
Copy link
Contributor

jhoeller commented Aug 7, 2020

I got an implementation that's pretty straightforward, can easily make 5.2.9...

@jhoeller jhoeller added type: bug A general bug for: backport-to-5.1.x Marks an issue as a candidate for backport to 5.1.x and removed type: enhancement A general enhancement labels Aug 7, 2020
@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-5.1.x Marks an issue as a candidate for backport to 5.1.x labels Aug 7, 2020
@jhoeller
Copy link
Contributor

jhoeller commented Aug 7, 2020

And since this is somewhat unintuitive indeed when switching to a lazy dependency proxy, I'll consider it a bug fix to backport...

@martinlippert
Copy link
Member

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.

@jhoeller
Copy link
Contributor

@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?

@BoykoAlex
Copy link
Author

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?

@jhoeller
Copy link
Contributor

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.

@BoykoAlex
Copy link
Author

@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 :-)

zx20110729 pushed a commit to zx20110729/spring-framework that referenced this issue Feb 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

6 participants