I am attempting to dynamically load some functionality based off of the environment my application is running in and was wondering if there is a pattern in spring to support this.
Currently my code looks something like this:
public interface DoThingInterface {
void doThing() {}
}
#Conditional(DoThingCondition.class)
#Component
public class DoThingService implements DoThingInterface {
#Override
public doThing() {
// business logic
}
}
#Conditional(DoNotDoThingCondition.class)
#Component
public class NoopService implement DoThingInterface {
#Override
public doThing() {
// noop
}
}
public AppController {
#Autowire
private DoThingInterface doThingService;
public businessLogicMethod() {
doThingService.doThing();
}
}
I appoligise for typing doThing so many times.
But as it currently stands with this, Spring cannot differentiate between the the NoopService and the DothingService since I am autowiring in an interface that both use. The conditionals that they use are directly opposed so there will only ever be one, but Spring does not know this. I had considered using #Profile() instead of conditional, but both will be used in a lot of environment. Is there a correct way to do this so that spring will load only one of these depending on the environment it is in?
Edit: For clarification this functionality is only available in certain deployment regions which is why I chose to use the conditional annotation as the conditions check profile, region, and properties.
As requested, the Conditions are as follows:
public class DoNotDoTheThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return !(region.equals(region) && profile.contains("prod"))
}
}
public class DoThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return (region.equals(region) && profile.contains("prod"))
}
}
I have simplified the conditions a bit, but that is the general idea. With the code in the state outlined here, Spring throws the following error: no qualifying bean of type DoThingInterface available: expected single matching bean, but found two: DoThingService, NoopService
The solution I came to was to use the condition and manually create the beans as per the comment by Thomas Kasene. I am still unsure why the original did not work, but the key bit was moving the #Conditional annotations onto the beans inside the config. My biggest problem with this method is that you have to maintain parody between the two conditions. That aside it makes for incredibly easy testing as you do not have to stub the noop service if you add your testing profile to the conditions.
The solution ended up looking like this:
Conditions
public class DoNotDoTheThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return !(region.equals(region) && profile.contains("prod"))
}
}
public class DoThingCondition implements Condition {
#Override
public boolean matches(ConditionalContext context) {
return (region.equals(region) && profile.contains("prod"))
}
}
Config
#Configuration
public class DoThingConfiguration {
#Conditional(DoThingCondition.class)
#Bean
public DoThingService doThingService() { return new DoThingService(); }
#Conditional(DoNotDoThingCondition.class)
#Bean
public NoopService noopService() { return new NoopService(); }
}
Services
public interface DoThingInterface {
void doThing() {};
}
public class DoThingService {
public void doThing() { // business logic }
}
public class NoopService {
public void doThing() { //Noop }
}
Controller
public class AppController {
private DoThingInterface doThingService;
public businessLogicMethod() {
doThingService.doThing();
}
}
Related
Why boolean matches(...) method in SpecificCaseCondition class is invoked 2 times ? I expect it to be invoked only once, on AnyConfiguration creation. In fact, it's invoked 2 times.
public class SpecificCaseCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
#Configuration
#Conditional(SpecificCaseCondition.class)
public class AnyConfiguration {
#Bean
public Service firstService() {
return new RealService();
}
#Bean
public Service secondService() {
return new RealService();
}
#Bean
public Service thirdService() {
return new RealService();
}
}
When Spring Boot auto-configures the beans for the app context, it does so in multiple phases. By default, condition at the class level* will be evaluated multiple times, for each phase. That's probably why you're seeing your custom Condition method being called more than once; Spring is invoking it during each phase.
One way to avoid that would be to annotate the #Bean methods with #Conditional instead of the entire class. Like this:
#Configuration
public class AnyConfiguration {
#Bean
#Conditional(SpecificCaseCondition.class)
public Service firstService() {
return new RealService();
}
#Bean
#Conditional(SpecificCaseCondition.class)
public Service secondService() {
return new RealService();
}
#Bean
#Conditional(SpecificCaseCondition.class)
public Service thirdService() {
return new RealService();
}
}
In my experiments, method-level conditions are only evaluated during the REGISTER_BEAN phase.
There is a downside to this solution, of course - it isn't very DRY. As an alternative, you can change your condition to implement ConfigurationCondition which has a method, public ConfigurationPhase getConfigurationPhase(), to dictate which phase the condition should be evaluated in.
The various #Conditional* annotations can be placed at the class level or the method level.
I have a Service class defined like this,
#RequiredArgsConstructor
class SomeService<T extends AbstractResponse> {
private final ValidationService<T> validationService;
....
}
And I have two kinds of AbstractResponse, ResponseA and ResponseB and have a validation service defined for both of them.
#Service("aValidationService");
class AValidationService<ResponseA> implements ValidationService<ResponseA> {
....
}
and
#Service("ValidationService");
class BValidationService<ResponseB> implements ValidationService<ResponseB> {
....
}
Right now spring is throwing an error because it's not able to deduce the implementation of ValidationService to use in SomeService as there are two implementations of it. How do I make spring deduce the correct implementation based on the type of AbstractResponse?
Hope that I understood your requirements.
You can not automatically inject, when you have (2) of the same kind. In this case ValidationService.
You could inject #ValidationServiceA, or #ValidationServiceB, or a List<ValidationServiceI> and then return the one you want based on a <T> type you care about:
The solution below highlights that.
The method getGenericParameter() is used to return the <T> parameter. This is to avoid the use of Reflection.
The method methodWhichDeterminesWhichServiceToUseBasedOnResponseType to used to determine which ValidationService to use based on the input that you require.
You can find the complete solution below, including a verification Test.
import org.springframework.stereotype.Service;
#Service
public class ValidationServiceA implements ValidationServiceI<ResponseA>{
#Override public Class<ResponseA> getGenericParameter() {
return ResponseA.class;
}
public void print(){
System.out.println("Service A");
}
}
#Service
public class ValidationServiceB implements ValidationServiceI<ResponseB>{
#Override public Class<ResponseB> getGenericParameter() {
return ResponseB.class;
}
public void print(){
System.out.println("Service B");
}
}
public interface ValidationServiceI<T>{
Class<T> getGenericParameter();
void print();
}
#Service
public class ServiceWhichCallsOthers {
#Autowired
private List<ValidationServiceI> validationServices;
public <T> ValidationServiceI<T> methodWhichDeterminesWhichServiceToUseBasedOnResponseType(T responseType){
Optional<ValidationServiceI> validationServiceSupportingResponse = validationServices.stream().filter(validationServiceI -> validationServiceI.getGenericParameter().equals(responseType)).findFirst();
return validationServiceSupportingResponse.get();
}
public void callValidationServiceA(){
methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseA.class).print();
}
public void callValidationServiceB(){
methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseB.class).print();
}
}
#SpringBootTest
public class ServiceWhichCallsOthersIT {
#Autowired
private ServiceWhichCallsOthers serviceWhichCallsOthers;
#Test
public void validateBasedOnResponseType(){
Assertions.assertEquals(ValidationServiceA.class, serviceWhichCallsOthers.methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseA.class).getClass());
Assertions.assertEquals(ValidationServiceB.class, serviceWhichCallsOthers.methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseB.class).getClass());
serviceWhichCallsOthers.callValidationServiceA();
serviceWhichCallsOthers.callValidationServiceB();
}
}
I have a question about SOLID design principles in the context of Spring dependency injection with generic type usage. I have the next generic interface:
public interface EmailChecker<T> {
boolean check(T message);
}
Then I have two implementations: one for production and one for the staging environment
#Profile("!production")
#Component
public class EmailRequestCheckerStaging implements EmailChecker<EmailRequest> {
#Override
public boolean check(EmailRequest message) {
//TODO: some code here;
return result;
}
}
#Profile("production")
#Component
public class EmailRequestCheckerProduction implements EmailChecker<EmailRequest> {
#Override
public boolean check(EmailRequest message) {
//TODO: some code here;
return result;
}
}
And finally over here is a dependency injection of 'emailChecker' field:
#Service
public class Receiver {
#Autowired
private EmailChecker<EmailRequest> emailChecker;
public void receiveMessage(EmailRequest dto) {
if(emailChecker.check(dto)) {
//TODO: some logic here
}
}
}
Question: does such dependency injection follows all SOLID principles?
Note: I have multiple implementations of 'EmailChecker' interface with different types (for simplicity I described 'EmailRequest' DTO implementations)
While opinion based, the injected dependency should be explicit via constructor injection.
#Service
public class Receiver {
private EmailChecker<EmailRequest> emailChecker;
#Autowired //could actually be omitted since class has only one constructor
public Receiver(EmailChecker<EmailRequest> emailChecker) {
this.emailChecker = emailChecker;
}
public void receiveMessage(EmailRequest dto) {
if(emailChecker.check(dto)) {
//TODO: some logic here
}
}
}
So that consumers of the Receiver class are aware of what it needs in order for it to function correctly
Classes with explicit dependencies are more honest about what they need. They state very clearly what they require in order to perform their particular function.
Let's say I have a custom ConstraintValidator:
public class FooValidator implements ConstraintValidator<ValidFoo, String> {
#Override
public void initialize(final ValidFoo foo) {
// No-op
}
#Override
public boolean isValid(final String foo, final ConstraintValidatorContext context) {
}
}
I'd like to be able to initialize this class by passing some configuration from the ServiceConfiguration in Dropwizard run or initialize.
Is this possible?
First, it's worth noting that the upcoming Dropwizard 2.0.0 release has built in support for this
For now, the process is a bit involved. You basically want to re-bootstrap the Hibernate validation but with a custom constraint validator factory that would support injection.
It's gonna involve about 4 custom classes, so bear with me. Here goes:
First, we start by registering a custom feature to wrap this functionality, into our Application class:
public void run(MainConfiguration config, Environment environment) throws Exception {
// ...
environment.jersey().register(InjectingValidationFeature.class);
}
Now we define the feature: InjectingValidationFeature - it basically registers our custom implementations within the service container:
public class InjectingValidationFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(ValidatorFactory.class).to(Validator.class).in(Singleton.class);
bind(InjectingConfiguredValidator.class).to(ConfiguredValidator.class).in(Singleton.class);
bind(InjectingConstraintValidatorFactory.class).to(ConstraintValidatorFactory.class).in(Singleton.class);
}
});
return true;
}
}
Now we define those classes that we are registering above. Let's start with the core piece, the InjectingConstraintValidatorFactory which is what Hibernate Validator will actually use to create the constraint validators. Note that because we are registering them in the container, we can actually start injecting stuff already, here is our custom ConstraintValidatorFactory making use of the service locator to make dependency injection possible:
public class InjectingConstraintValidatorFactory implements ConstraintValidatorFactory {
private final ServiceLocator serviceLocator;
#Inject
public InjectingConstraintValidatorFactory(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
return this.serviceLocator.createAndInitialize(key);
}
#Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
this.serviceLocator.preDestroy(instance);
}
}
Now our factory for the central javax.validation.Validator interface:
public class ValidatorFactory implements Factory<Validator> {
private final ConstraintValidatorFactory constraintValidatorFactory;
#Inject
public ValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
this.constraintValidatorFactory = constraintValidatorFactory;
}
#Override
public Validator provide() {
return Validation.byDefaultProvider().configure().constraintValidatorFactory(
this.constraintValidatorFactory).buildValidatorFactory()
.getValidator();
}
#Override
public void dispose(Validator instance) {
// Nothing
}
}
And finally, our InjectingConfiguredValidator, notice how it's just using DropwizardConfiguredValidator but with an #Inject which would allow us to receive the validator from our ValidatorFactory above:
public class InjectingConfiguredValidator extends DropwizardConfiguredValidator {
#Inject
public InjectingConfiguredValidator(Validator validator) {
super(validator);
}
}
That's it. With the above, we managed to both register an injection-aware Validator with Jersey and also into our service container so you can also #Inject Validator anywhere and use it however you like.
I have tried to use the same class both for tests definition and for spring context configuration.
Below is code for class CombineTestAndConfigTry which serves both as test definition and context definition for itself.
bean1 is just stub bean. bean2 should contain the name of the class and bean3 should contain a reference to a class.
It is evident, that Spring is wrapping test class instance into different class, so tests are failed.
Simultaneously, it appeared, that some information can be passed from test class to spring context.
The question is: how normal is such usage and which problems can I meet if utilize it?
Also it is interesting (and how to know), how many times #Autowired is executed? Two times because of both roles? Or one time because Spring sees the situation?
#Configuration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = CombineTestAndConfigTry.class)
public class CombineTestAndConfigTry {
public static class MyBean1 {
{
System.out.println("MyBean1 constructor");
}
}
public static class MyBean2 {
private String configName;
{
System.out.println("MyBean2 constructor");
}
public String getConfigName() {
return configName;
}
public void setConfigName(String configName) {
this.configName = configName;
System.out.println("MyBean2#configName set");
}
}
public static class MyBean3 {
private CombineTestAndConfigTry testInstance;
{
System.out.println("MyBean3 constructor");
}
public CombineTestAndConfigTry getTestInstance() {
return testInstance;
}
public void setTestInstance(CombineTestAndConfigTry testInstance) {
this.testInstance = testInstance;
System.out.println("MyBean3#testInstance set");
}
}
public String getConfigName() {
return getClass().getSimpleName();
}
#Bean
public MyBean1 myBean1() {
return new MyBean1();
}
#Bean
public MyBean2 myBean2() {
MyBean2 ans = new MyBean2();
ans.setConfigName( getConfigName() );
return ans;
}
#Bean
public MyBean3 myBean3() {
MyBean3 ans = new MyBean3();
ans.setTestInstance(this);
return ans;
}
#Autowired
public MyBean1 myBean1;
#Autowired
public MyBean2 myBean2;
#Autowired
public MyBean3 myBean3;
#Test
public void testGetConfigName() {
assertEquals( getConfigName(), myBean2.getConfigName() );
}
#Test
public void testGetTestInstance() {
assertSame(this, myBean3.getTestInstance());
}
}
My answer to your question is: don't do it (this way).
One of the most important rules of "clean coding" (see the book by Robert Martin) is the SRP - single responsibility principle. Any class (and any method within) is there to do one thing; and one thing only. Meaning: "need of change" should always be coming from one "source"; and not several.
Long story short: what you are asking for is considered (very) bad practice.
If you are doing it in order to avoid another principle DRY (dont-repeat-yourself) ... then think about ways to change your design.