Description
Proposal
At the moment, ConnectionDetailsFactories
initializes a SpringFactoriesLoader
using its own class loader (ConnectionDetailsFactories.class.getClassLoader()
). I propose instead that the SpringFactoriesLoader
be initialized with the thread's context class loader (Thread.currentThread().getContextClassLoader()
) by default - falling back to the previous behavior if the context class loader is missing.
Motivation
When defining our own ConnectionDetails
and ConnectionDetailsFactory
in a project that uses spring-boot-devtools
, the ConnectionDetails
instance created by the factory fails to be autowired. This happens because the class loader used by the factory differs from the one used to wire the bean.
Example
src/main/java/com/example/custom/demo/CustomPGConnectionDetails.java
public interface CustomPGConnectionDetails extends ConnectionDetails {
String getHost();
}
src/main/java/com/example/custom/demo/CustomPGDockerComposeConnectionDetailsFactory.java
class CustomPGDockerComposeConnectionDetailsFactory
extends DockerComposeConnectionDetailsFactory<CustomPGConnectionDetails> {
private static final String[] CUSTOM_PG_CONTAINER_NAMES = { "postgres" };
CustomPGDockerComposeConnectionDetailsFactory() {
super(CUSTOM_PG_CONTAINER_NAMES);
}
@Override
protected CustomPGConnectionDetails getDockerComposeConnectionDetails(final DockerComposeConnectionSource source) {
return new CustomPGDockerComposeConnectionDetails(source.getRunningService());
}
static class CustomPGDockerComposeConnectionDetails extends DockerComposeConnectionDetails
implements CustomPGConnectionDetails {
private final String host;
private CustomPGDockerComposeConnectionDetails(final RunningService runningService) {
super(runningService);
this.host = runningService.host();
}
@Override
public String getHost() {
return host;
}
}
}
src/main/java/com/example/custom/demo/CustomPGClient.java
package com.example.demo.custom;
import org.springframework.stereotype.Service;
@Service
class CustomPGClient {
private final CustomPGConnectionDetails customPGConnectionDetails;
public CustomPGClient(final CustomPGConnectionDetails customPGConnectionDetails) {
this.customPGConnectionDetails = customPGConnectionDetails;
}
}
src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=com.example.demo.custom.CustomPGDockerComposeConnectionDetailsFactory
In this example, the application will fail to start with the following error:
Parameter 0 of constructor in com.example.demo.custom.CustomPGClient required a bean of type 'com.example.demo.custom.CustomPGConnectionDetails' that could not be found.
Disclaimer
My understanding of the inner workings of Spring is limited, so there might be a good reason this isn't already done.
If that's the case, please let me know so we can explore alternatives or consider opening a broader issue to address the underlying problem.