Reputation: 8074
I have a Spring application consisting of multiple modules. One of these modules requires certain Spring beans to be present in the context (it cannot run standalone as it does not have a complete context itself).
This module provides basic functionality that needs to be shared amongst many applications that customize this module by making the correct beans available (singleton or request scoped, depending on needs).
This works perfectly and we're very happy with this setup as it provides a seperation between core functionality and business specific logic.
My question is now, I have a class that can optionally be used to satisfy one of the depedencies. It is not annotated with @Component to prevent it being scanned, however I would like the projects to be able to choose to use this class or supply their own implementation.
The core module looks like this:
public interface AProvider;
@Component
public class AService {
@Inject private AProvider aProvider;
}
And it provides this implementation that can optionally be used:
public class DatabaseBasedAProvider implements AProvider {
@Inject private SomeOtherDependency dependency; // <-- this needs to be injected still if used!
}
An example project that uses the core module then must make sure that one bean of type AProvider
is present on the context. This can be achieved like:
@Configuration
public class Configuration {
@Bean
AProvider getAProvider() {
return new OurOwnAProviderImplementation();
}
}
What I would like though is something like:
@BeanClass // <-- some annotation I made up
Class<AProvider> getAProviderClass() {
return DatabaseBasedAProvider.class; // <-- have spring inject this!
}
What I don't want is:
@Bean
AProvider getAProvider() {
return new DatabaseBasedAProvider( ... add dependencies here myself ... );
}
Upvotes: 2
Views: 1260
Reputation: 8074
I've found a solution that allows me to decide at the client what class I want to use for AProvider
.
It is not super nice, but it does mean I don't need to make specific changes to the code in the core module (as this module is supposed to be generic).
In a @Configuration class in the client's config I'm now doing this:
@Component
static class MyDatabaseBasedAProvider extends DatabaseBasedAProvider {
// No implementation
}
This makes Spring construct the class and handle all the injections. It could be shorter and it does require the class to be non-final but it works.
The client is now alerted if the bean is missing, is free to make their own implementation and free to pick one of the existing implementations if one suits their needs, without the core module having to decide before hand how AProvider
might be supplied.
Upvotes: 0
Reputation: 3106
An alternative will be to use @Profile, another alternative would be to annotate your AProvider classes with @Component in combination with @ConditionalOnProperty and document the different choices to your consumers.
Example
@Component
@ConditionalOnProperty(name = "my.aprovider.choice", havingValue = "database")
public class DatabaseBasedAProvider implements AProvider {
@Inject private SomeOtherDependency dependency; // <-- this needs to be injected still if used!
}
Upvotes: 0
Reputation: 12225
I have solved a case similar to yours (if I understand correctly), using the @Primary
annotation. Might be something for you.
public interface AProvider { }
For every module to have some implementation of the interface, create a default implementation that is shared.
@Service
public class DefaultAProvider implements AProvider {}
Then, if some module wishes to use its own implementation, "override" the bean using @Primary
.
@Primary
@Service
public class MyVerySpecialAProvider implements AProvider {}
Then, anytime you inject AProvider
, Spring
will pick the @Primary
implementation.
Upvotes: 1