2

We've written an in-house service discovery (SD) client based on the spring-cloud-commons SPI, meaning it provides implementations for interfaces ServiceRegistry and DiscoveryClient and some other Spring-provided abstractions.

Apps that use our library merely add it to their pom file, and it autowires the DiscoveryClient with its own implementation, InHouseDiscoveryClient

<dependency>
   <groupId>blah.blah<groupId>
   <artifactId>inhouse-service-discovery-client<artifactId>
<dependency>

However, rather than referring to InHouseDiscoveryClient in code, it's best practices to use the interface DiscoveryClient as shown below

# Good 
@Autowired
DiscoveryClient client;

# Bad (binds app to a particular SD implementation)
@Autowired
InHouseDiscoveryClient client;

As such, we are required to add spring-cloud-commons to the project.

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
    </dependency>

This is where the issue starts. The commons library actually autowires two additional implementations of DiscoveryClient - the SimpleDiscoveryClient and the CompositeDiscoveryClient.

This makes for an odd user experience for our clients. Instead of simply having InHouseDiscoveryClient, users find themselves with these additional beans.

Is it possible to prevent spring-cloud-commons's DiscoveryClient implementations from autowiring? And if so, can this be done in our library rather than in the end-user's applications?

Adam Hughes
  • 14,601
  • 12
  • 83
  • 122
  • Why are the other beans a problem? – spencergibb Feb 22 '19 at 05:41
  • Well - it's more of a source of confusion than anything. For example - say we implemented the `description()` method of `InHouseDiscoveryClient` to return something informative like the email address for our support team. Users call this method and get "Composite Discovery Client" and I anticipate they would be confused by this and possibly think something is wrong w/ their config. So we'll either need to clearly describe this behavior in our documentation (ie. that 3 beans are autowiring and not 1). What is the use-case for having multiple clients, maybe I don't understand? – Adam Hughes Feb 22 '19 at 06:15
  • 1
    I'm trying to determine if there is a technical problem or not. Users wanted to be able to sidestep a discovery system and put entries in configuration for various reasons. https://github.com/spring-cloud/spring-cloud-commons/issues/140 it's going on a few years old. – spencergibb Feb 23 '19 at 19:26
  • To turn them off you need to exclude https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/composite/CompositeDiscoveryClientAutoConfiguration.java and https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/simple/SimpleDiscoveryClientAutoConfiguration.java via properties `spring.autoconfigure.exclude` – spencergibb Feb 23 '19 at 19:27
  • Thanks @spencergibb - I had actually done this using ExclusionFilter - have updated answer. I don't think there's a technical problem per-se, but the linked issue seems like a specialized usecase which perhaps should be opt-in? I'd be happy to discuss my use case at more length if that would help, but to be honest we just wanted to use the commons interfaces/abstractions and found some of the default implementations to be in the way. Perhaps it would be possible to split the commons abstractions into a standalone submodule? – Adam Hughes Feb 23 '19 at 23:22

1 Answers1

0

I ended up extended AutoConfigurationImportFilter in my library to remove the autowired beans from cloud-commons. I also removed it's health indicator, but we had a very particular reason to do so - most probably would rather keep it.

my.package

public class StratusDiscoveryExclusionFilter implements AutoConfigurationImportFilter {

private static final Set<String> SHOULD_SKIP = new HashSet<>(
        Arrays.asList(
                // DiscoveryClient Beans
                "org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration",
                "org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration",
                // Health indicators
                "org.springframework.cloud.client.CommonsClientAutoConfiguration")
);

/**
 * For each class name, provide an assocated boolean array indicated whether or not to include
 */
@Override
public boolean[] match(String[] classNames, AutoConfigurationMetadata metadata) {
    boolean[] matches = new boolean[classNames.length];

    for (int i = 0; i < classNames.length; i++) {
        matches[i] = !SHOULD_SKIP.contains(classNames[i]);
    }
    return matches;
 }
}

I think add a reference to this in my library's spring.factories file

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=my.package.MyExclusionFilter
Adam Hughes
  • 14,601
  • 12
  • 83
  • 122