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

Use thread's context class loader for spring factories loader on connection details factory #45010

Open
lengors opened this issue Apr 5, 2025 · 0 comments
Labels
status: waiting-for-triage An issue we've not yet triaged

Comments

@lengors
Copy link

lengors commented Apr 5, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged
Projects
None yet
Development

No branches or pull requests

2 participants