I have a Spring based Java application where a lot of classes use the following autowired interface.. they work off this interface at all places.
#Autowired
private IOperatingSystemManager m_operatingSystemManager;
Right now, there is only one implementation of the interface as follows:
#Component
public class WindowsManager implements IOperatingSystemManager
{
// Windows based shenanigans
}
And the application works as expected. Spring is happy. Everybody is happy.
Alright, not everybody...
So, I want to add another concrete implementation of IOperatingSystemManager ..
#Component
public class LinuxManager implements IOperatingSystemManager
{
// Linux based shenanigans
}
What we want is the auto wiring of IOperatingSystemManager conditionally based on a properties file setting. (say.. os=windows.. basically something that is an arbitrary string and cannot be derived from system properties etc. simply because this is a dummy example. the actual managers are not OS related.)
I don't want to change any of the classes who have autowired to the interface and are working off the interface. All I need is for Spring to look at some logic that will dictate the Autowiring of the variables and wire up the right concrete instance for:
#Autowired
IOperatingSystemManager m_operatingSystemManager
at all the gazillion places.
The documentation & web search talk about profiles, condition, bean factory, qualifiers etc.. but we don't want to use Profiles; and Qualifiers seem to be needing changes to all the interface variable annotations.
Factory methods look promising, but being new to Spring, couldn't find a crisp answer.
What is a simple and recommended way to achieve this?
Instead of scanning the WindowsManager class, create one concrete instance that implements the IOperatingSystemManager interface or another one, depending on the your logical conditions.
First, remove the #Component annotation from the WindowsManager class.
Then, create and scan this #Configuration class, which will act as a factory for your beans:
#Configuration
public class OperatingSystemManagerFactory {
#Bean
public IOperatingSystemManager getOperatingSystemManager() {
if ( /* some logic that evaluates to true if windows */ ) {
return new WindowsManager();
} else {
// Linux default option ;)
return new LinuxManager();
}
}
}
With this solution, you shouldn't need to update anyone of your classes that reference the IOperatingSystemManager interface.
I dont know which version of spring you are using but you have options for this
http://www.intertech.com/Blog/spring-4-conditional-bean-configuration/
Here, as you can see, you can create a bean based on a condition that you can decide. It actully gave your example, Windows and Linux :), so i believe thats what you are looking for.
Edit:
If you are using spring-boot, you have some other Conditional annotations
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html#boot-features-condition-annotations
Related
In a simple Spring boot application I have my component like this:
#Data
#Component
public class GenericHandler {
private String path;
private HandlerType type;
}
And my properties might look like this:
my.handlers[0].path='/vol1/abc'
my.handlers[0].type='Single'
my.handlers[1].path='/vol1/dora'
my.handlers[1].type='MultiSequence'
I tried decorating with the GenericHandler-class with #ConfigurationProperties(prefix="my.handlers") and getting a list of all component instances in a service using
#Autowired
private List<GenericHandler> handlers;
But that created just one component, ignoring the property values at all.
How can I get one component instance per my.handlers property-entry?
You need a wrapper class
#Component
#ConfigurationProperties(prefix="my.handlers")
#Data
public class GenericHandlerWrapper {
private List<GenericHandler> handlers;
...
}
Then you can autowire the GenericHandlerWrapper
Update
As #zoolway pointed out in the comments, for the properties in the question to work as it is, #ConfigurationProperties(prefix="my.handlers") should be changed to #ConfigurationProperties(prefix="my")
That's not possible. What can be done is this:
#Data
#Component
public class GenericHandler {
private List<String> path;
private List<HandlerType> type;
}
I dealt with a similar issue in a different manner. I created a factory and an interface. The factory would hold different implementations of that interface In your case, GenericHandler would be your interface. Then you write any number of implementations of your interface and each implementation is declared as a Component. So, Spring will instantiate it as bean upon a startup (you might use #Lazy(false) to force the instantiation at startup) using some infrastructure that I wrote each bean of that interface will self-insert itself into its factory. Then at any part of your code in any bean, you can use the factory to access concrete implementation (base on your property "type" for example). The beauty is that you don't need to inject all the implementations in your bean at the time of writing but access needed implementation dynamically at run-time. I found this to be a useful pattern and created an infrastructure that does most of the work for you and published it as an Open Source library called MgntUtils. The detailed description of the idea (including reference to the library) could be found here. Also detailed explanation with examples of how to use it can be found in library Javadoc here. The library is available (with source code and Javadoc) as Maven artifacts and on the Github. Also a general article about the MgntUtils library could be found here
Is it possible to configure a bean in such a way that it wont be used by a group of profiles? Currently I can do this (I believe):
#Profile("!dev, !qa, !local")
Is there a neater notation to achieve this? Let's assume I have lots of profiles. Also, if I have a Mock and concrete implementation of some service (or whatever), Can I just annotate one of them, and assume the other will be used in all other cases? In other words, is this, for example, necessary:
#Profile("dev, prof1, prof2")
public class MockImp implements MyInterface {...}
#Profile("!dev, !prof1, !prof2") //assume for argument sake that there are many other profiles
public class RealImp implements MyInterface {...}
Could I just annotate one of them, and stick a #Primary annotation on the other instead?
In essence I want this:
#Profile("!(dev, prof1, prof2)")
Thanks in advance!
Since Spring 5.1 (incorporated in Spring Boot 2.1) it is possible to use a profile expression inside profile string annotation (see the description in Profile.of(..) for details).
So to exclude your bean from certain profiles you can use an expression like this:
#Profile("!dev & !prof1 & !prof2")
Other logical operators can be used as well, for example:
#Profile("test | local")
Short answer is: You can't in versions of Spring prior to Spring 5.1 (i.e. versions of Spring Boot prior to 2.1).
But there is a neat workarounds that exists thanks to the #Conditional annotation.
Create Condition matchers:
public static abstract class ProfileCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (matchProfiles(conditionContext.getEnvironment())) {
return ConditionOutcome.match("A local profile has been found.");
}
return ConditionOutcome.noMatch("No local profiles found.");
}
protected static abstract boolean matchProfiles(final Environment environment);
}
public class DevProfileCondition extends ProfileCondition {
protected boolean matchProfiles(final Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
return prof.equals("dev") || prof.equals("prof1") || prof.equals("prof2");
});
}
}
public static class ProdProfileCondition extends ProfileCondition {
protected boolean matchProfiles(final Environment environment) {
return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
return (!prof.equals("dev") && !prof.equals("prof1") && !prof.equals("prof2"));
});
}
}
Use it
#Conditional(value = {DevProfileCondition.class})
public class MockImpl implements MyInterface {...}
#Conditional(value = {ProdProfileCondition.class})
public class RealImp implements MyInterface {...}
However, this aproach requires Springboot.
From what I understand, what you want to do is be capable of replacing some of your beans with some stub/mock beans for specific profiles. There are 2 ways to address this:
Exclude the not needed beans for the corresponding profiles and include by default everything else
Include only the required beans for each profile
The first option is feasible but difficult. This is because the default behaviour of Spring when providing multiple profiles in #Profile annotation is an OR condition (not an AND as you would need in your case). This behaviour of Spring is the more intuitive, because ideally each profile should correspond to each configuration of your application (production, unit testing, integration testing etc.), so only one profile should be active at each time. This is the reason OR makes more sense than AND between profiles. As a result of this, you can work around this limitation, probably by nesting profiles, but you would make your configuration very complex and less maintainable.
Thus, I suggest you go with the second approach. Have a single profile for each configuration of your application. All the beans that are the same for every configuration can reside in a class that will have no #Profile specified. As a result, these beans will be instantiated by all the profiles. For the remaining beans that should be distinct for each different configuration, you should create a separate #Configuration class (for each Spring profile), having all of them with the #Profile set to the corresponding profile. This way, it will be really easy to tract what is injected in every case.
This should be like below:
#Profile("dev")
public class MockImp implements MyInterface {...}
#Profile("prof1")
public class MockImp implements MyInterface {...}
#Profile("prof2")
public class MockImp implements MyInterface {...}
#Profile("the-last-profile") //you should define an additional profile, not rely on excluding as described before
public class RealImp implements MyInterface {...}
Last, #Primary annotation is used to override an existing beans. When there are 2 beans with the same type, if there is no #Primary annotation, you will get an instantiation error from Spring. If you define a #Primary annotation for one of the beans, there will be no error and this bean will be injected everywhere this type is required (the other one will be ignored). As you see, this is only useful if you have a single Profile. Otherwise, this will also become complicated as the first choice.
TL;DR: Yes you can. For each type, define one bean for each profile and add a #Profile annotation with only this profile.
I need to dynamically Inject a variable group of classes in my application. The purpose is, as the application grows, only have to add more classes inheriting the same interface. This is easy to do with tradicional java as I just need to search for all classes in a package and perform a loop to instantiate them. I want to do it in CDI. For example:
public MyValidatorInterface {
public boolean validate();
}
#Named
MyValidator1 implements MyValidatorInterface
...
#Named
MyValidator2 implements MyValidatorInterface
...
Now the ugly non real java code just to get the idea of what I want to do:
public MyValidatorFactory {
for (String className: classNames) {
#Inject
MyValidatorInterface<className> myValidatorInstance;
myValidatorInstance.validate();
}
}
I want to loop over all implementations found in classNames list (all will be in the same package BTW) and Inject them dynamically so if next week I add a new validator, MyValidator3, I just have to code the new class and add it to the project. The loop in MyValidatorFactory will find it, inject it and execute the validate() method on the new class too.
I have read about dynamic injection but I can't find a way to loop over a group of class names and inject them just like I used to Instantiate them the old way.
Thanks
What you are describing is what Instance<T> does.
For your sample above, you would do:
`#Inject Instance<MyValidatorInterface> allInstances`
Now, allInstances variable contains all your beans which have the given Type (MyValidatorInterface). You can further narrow down the set by calling select(..) based on qualifiers and/or class of bean. This will again return an Instance but with only a subset of previously fitting beans. Finally, you call get() which retrieves the bean instance for you.
NOTE: if you call get() straight away (without select) in the above case, you will get an exception because you have two beans of given type and CDI cannot determine which one should be used. This is implied by rules of type-safe resolution.
What you most likely want to know is that Instance<T> also implements Iterable so that's how you get to iterate over the beans. You will want to do something like this:
#Inject
Instance<MyValidatorInterface> allInstances;
public void validateAll() {
Iterator<MyValidatorInterface> iterator = allInstances.iterator();
while (iterator.hasNext()) {
iterator.next().callYourValidationMethod();
}}
}
I have created a OSGI service with declarative services to inject an object that implements an interface. If I inject the object in a class that is attached to the application model (handler,part,....) it is working fine. If I inject it in a class that is not attached to the application model it is always returning null.
Is it possible to use DI in classes that are not attached to the application model? I looked in the vogella tutorials but somehow I don't find a solution.
I know of three ways of how Eclipse 4 can inject objects in your classes:
During start-up the Eclipse runtime looks for relevant annotations in the classes it instantiates.
Objects injected in 1. are tracked and will be re-injected if changed.
Manually triggering injection using the ContextInjectionFactory and IEclipseContext.
What you want may be possible with the third option. Here is a code example:
ManipulateModelhandler man = new ManipulateModelhandler();
//inject the context into an object
//IEclipseContext iEclipseContext was injected into this class
ContextInjectionFactory.inject(man,iEclipseContext);
man.execute();
The problem is, however; that the IEclipseContext already needs to be injected into a class that can access the object that needs injection. Depending on the number of necessary injections, it might be more useful to use delegation instead (testability would be one argument).
#Inject
public void setFoo(Foo foo) {
//Bar is not attached to the e4 Application Model
bar.setFoo(foo);
}
Therefore, a better solution is probably using the #Creatable annotation.
Simply annotate your class, and give it a no-argument constructor.
#Creatable
public class Foo {
public Foo () {}
}
Using #Inject on that type as in the method above, will let Eclipse instantiate and inject it.
The disadvantage is that you cannot control the object creation anymore, as you would with ContextInjectionFactory.inject(..).
I refactored out some part of e(fx)clipse in order to achieve that. Have a look at this. Sorry for the shameless plug...
Background: I'm using Google Guice and so it's easier to pass through the configuration class but I think this is not the best way.
I have a configuration class which stores some paths:
class Configuration{
String getHomePath();
String getUserPath();
}
Also I have a class "a" which needs the "homepath" and a class "b" which needs the "userpath".
Is it better to pass the configuration class through the constructor of class a and b or only pass through the specific path?
If you're really using Guice correctly all your configuration like this should appear in modules' configure method. So:
Remove the configuration class.
Create annotation classes, probably called HomePath and UserPath.
Where class a uses getHomePath() replace that with a String field member named homePath.
Where class b uses getUserPath() replace that with a String field member named userPath.
Modify the class a and b constructors to be #Inject annotated (should already be) and take in a String parameter, respectively annotated with #HomePath and #UserPath and assign the String field member that injected value.
Create bindings in your module's configure method use .annotatedWith() which define correct values; if they're only available at run time, bind a provider.
E.G.
class a {
private String homePath;
#Inject
public a(#HomePath String homePath) {
this.homePath = homePath;
}
public String tellMeAboutHome() {
return "We live in a nice home called " + homePath;
}
}
class customModule extends AbstractModule {
public static final String userPath = "/home/rafael";
public void configure() {
bind(String.class).annotatedWith(HomePath.class).to("/home/");
bind(String.class).annotatedWith(UserPath.class).to(userPath);
}
}
If creating annotations is too much work for you, use the #Named annotation Guice ships with.
There's no single answer to your question, there are only options to choose from, based on your specific situation.
If you know your Configuration class is going to grow AND if it's likely for your A and B classes will use more from it, then pass the whole Configuration object to their constructors. NB: I know this is against the YAGNI principle but sometimes you may know you're gonna need it ;-)
Otherwise, you can consider using #Named injection of your paths so that you reduce A and B classes dependencies to their minimum, which is a good design practice.
The general rule is code to make the dependency graph (which classes know about or depend on other classes/ interfaces) as simple, regular and fixed as possible.
If not passing the Configuration class makes a or b have zero dependencies on on user-written classes, or is necessary to avoid a dependency loop, then use the individual path strings. Otherwise, if it makes more sense to say 'this class has access to configuration info, in a way that may change in the future', pass the class.
I'd avoid the singleton approach, especially if you already have Guice set up.