Search code examples
javaspringspring-bootdesign-patternsautowired

How to provide support for custom implementation in a Java library while offering a default implementation?


I'm developing a Java-based library that includes a default implementation packaged within the JAR file. However, I also want to provide support for consumer applications to have their own custom implementation if needed.

The default implementation is known by class A, which is part of the library. The consumer application can write their own implementation using class B, as shown in the example below. In class B, there is a setHandler method that should override if the consumer application has its own implementation of E. If the consumer does not provide a custom implementation, the default implementation from class A should be used.

The packaged jar have default implementation A and used in class D

class A implements E{
    // Default implementation of E packed with jar
}

@Configuration
class D {
    
    @Autowired
    E e;
    
    public void config( HttpConfig conf ){

        conf.setHandler(e);
    }

}

The consumer class can have own implementation B which can implements E

class B implements E{
    // Implementation which source repository can have
}

How can I design the library to allow the consumer application to easily provide their own implementation for the setHandler method, overriding the default implementation from class E if necessary?


Solution

  • Use @ConditionalOnMissingBean. This would be how you would configure it in your library:

    @Configuration
    class Config {
            
      @Bean
      @ConditionalOnMissingBean
      public SomeInterface defaultSomeInterface() {
        return SomeImplementation();
      }
    
    }
    

    Then, your consumers can override it by just declaring their own definition of SomeInterface.

    If I were consuming your library, then I would just do this without the @ConditionalOnMissingBean annotation:

    @Configuration
    class MyConfig {
            
      @Bean
      public SomeInterface myImplementation() {
        return MyImplementationOfSomeInterface();
      }
    
    }
    

    Now, whenever Spring autowires SomeInterface, it will use MyImplementationOfSomeInterface.