With Spring 3.1 and profiles, creating a custom interface to define specific profiles becomes interesting. Part of the beauty is the ability to completely forgot the String name of the profile and just use the annotation.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Profile("Dev")
public #interface Dev {
}
And then simply annotate beans with #Dev. This works great.
However, how can I check if the #Dev profile is active? Environment.acceptsProfiles() requires a String argument. Is there a "neat" way of doing this, or is my only option to do something like:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Profile(Dev.NAME)
public #interface Dev {
public static String NAME = "Dev";
}
public class MyClass{
#Autowired
private Environment env;
private void myMethod(){
if( env.acceptsProfiles( Dev.NAME ) )
// do something here
;
}
Although functional, I'm not particularly fond of this concept. Is there another way I can do this neater?
I wanted to do something similar (in my case, represent a list of synonyms under one profile annotation) but I ran into the problem your having, as well as another limitation: You won't be able to apply more than one of the annotations to a single bean and have them both get picked up by spring (at least in spring 3).
Unfortunately, as you cannot pass the enum in, the solution I settled on was to just use plain-old string constants without the enum. Then I could do something like #Profile(CONSTANT_ONE, CONSTANT_TWO). I still benefited from not being able to make typos, but also gained the ability to still apply multiple profiles to the same bean.
Not perfect, but not too bad.
Related
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.
In Weld, I can do the following to get dynamically inject config values from some source:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER })
public static #interface ConfigValue {
#Nonbinding
String value();
}
#Produces
#Dependent
#ConfigValue("")
public String stringValue(InjectionPoint ip) {
ConfigValue configValue = ip.getAnnotated().getAnnotation(ConfigValue.class);
return myConfigMap.get(configValue.value());
}
The equivalent Spring, however, matches based on the value of the #ConfigValue annotation.
I would like Spring to call a method to allow me to inject custom values for all fields annotated with #ConfigValue.
I'm aware of this: http://joshlong.com/jl/blogPost/supporting_your_own_field_or_method_injection_annotation_processors_in_spring.html
However that's a very complex solution for a seemingly simple problem. I'm wondering if there's a simpler solution...
Spring has InjectionPoint class. The catch is that your #ConfigValue annotation should not have Qualifier annotation. Then you can have a single method to produces various values.
However, if you choose to have #Qualifier on your #ConfigValue annotation, then you will have to have multiple producing methods one for each value of #ConfigValue. I dont seem to have come across anything equivalent to #Nonbinding though.
I have tested this for user object, not String.
PS: another approach is to have one "defining" annotation and one "non-defining" annotation on the injection point to serve your need.
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
This is the first time I create an Annotation Java and I'd like to create a my own annotation then suppress the execution for a test when necessary.
The problem is that I many of my tests I have to use Facebook api, sometimes they don't work so I want an annotation called #Facebook that when added to a test works as #Suppress annotation, so I wrote the following code...that unfortunally doesn't work. Anyone can help me?
#Retention(RetentionPolicy.RUNTIME)
public #interface Facebook {
Suppress notToBeRun() default #Suppress;
}
Java contains a flexible annotation API with numerous application possibilities. First developed to specify enterprise semantics in the Java EE stack (whether a Java-bean is stateless or statefull, singleton, etc.), the annotation interface has now also found common use for Context Dependent Injection (CDI) in Java. Your question addresses how to use the Java annotation API for CDI.
First, you need to define a qualifier interface-class for each specific user-defined CDI-option you want Java to Inject. You want a Facebook-implementation to be loaded by injection. Your interface (Facebook.java) can look as follows:
#Qualifier
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface Facebook {
}
The first term, #Qualifier indicates that you define a new qualifier, actually a unique name (#Facebook) known by the Java injection mechanism.
The #Target annotation indicates that your annotation can precede a Java type declaration, a Java field (specifically a variable declaration) or a method parameter. You can add a fourth qualifier to allow your annotation also to be used before a method, namely ElementType.METHOD.
#Documented defines an annotation that assures that classes using this annotation show this in their generated JavaDoc. #Retention must be set to RetentionPolicy.RUNTIME in order for the annotation to become active when the Java-application is started (deployed, in web application server context).
You now need to define a general Java interface class (SocialMediaService.java), just a plain Java interface:
public interface SocialMediaService {
boolean login(String userId, String password);
void logout();
String searchForMessages(String[] friends);
}
This interface can be implemented in different ways, by means of the implements Java-construct.
Using the previously defined annotation, you can choose
in the Java-code which of the alternative implementations to use.
Here is the Facebook-example of a Java-class (Facebook.java, in a different package than the interface qualifier class, specified above):
#Facebook
public class Facebook implements SocialMediaService {
#Override
public boolean login(String userId, String password) {
...
your application logic
...
return true;
}
#Override
public void logout() {
...
your application logic
...
}
#Override
public String searchForMessages(String[] friends) {
...
your application logic
...
return searchResult;
}
}
You can choose among numerous different implementations #LinkedIn, etc. each with their specific Java implementation class (alternatives to public class Facebook).
In your Java-class you are now ready to use CDI to inject the Java implementation of choice.
Back-end Java-bean (BackendSocialMediaAnalysis.java) where CDI is being applied:
public class BackendSocialMediaAnalysis {
...
#Inject #Facebook
private SocialMediaService genericMediaService;
...
}
Replacing #Facebook by #LinkedIn results in the alternative (LinkedIn) implementation being loaded into genericMediaService.
Say I have an annotation with a property:
#Named(name = "Steve")
private Person person
and I want to create a compound annotation with several meta-annotations, including the one that takes a property
#Named
#AnotherAnnotation
#YetAnotherAnnotation
public #interface CompoundAnnotation {
...
}
Is there a way that I can pass properties to the compound annotation to one of the meta annotations?
Eg, something like this:
#CompoundAnnotation(name = "Bob")
private Person person;
that is equivalent to, but much more convenient than
#Named(name = "Bob")
#AnotherAnnotation
#YetAnotherAnnotation
private Person person;
Thanks!
PS apologies for my poor choice of an example annotation - I didn't have the javax.inject.#Named annotation in mind, just some arbitrary annotation that has properties.
Thank you everyone for your answers/comments.
It definitely seems to be the case that this is not possible. However, it just happens that there is a simple work-around for my case-in-point, which I will share in case it helps anyone:
I am working with Spring and want to create my own Annotations that have #Component as a meta-annotation, thus being autodetected by component scanning. However, I also wanted to be able to set the BeanName property (corresponding to the value property in #Component) so I could have custom bean names.
Well it turns out that the thoughtful guys at Spring made it possible to do just that - the AnnotationBeanNameGenerator will take the 'value' property of whatever annotation it is passed and use that as the bean name (and of course, by default, it will only get passed annotations that are #Component or have #Component as a meta-annotation). In retrospect this should have been obvious to me from the start - this is how existing annotations with #Component as a meta-annotation, such as #Service and #Registry, can provide bean names.
Hope that is useful to someone. I still think it's a shame that this is not possible more generally though!
It is a few years later now, and since you are using Spring, what you are asking for is sort of possible now using the #AliasFor annotation.
For example:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#SpringApplicationConfiguration
#ActiveProfiles("test")
public #interface SpringContextTest {
#AliasFor(annotation = SpringApplicationConfiguration.class, attribute = "classes")
Class<?>[] value() default {};
#AliasFor("value")
Class<?>[] classes() default {};
}
Now you can annotate your test with #SpringContextTest(MyConfig.class), and the amazing thing is that it actually works the way you would expect.
N.B. When you need to programmatically get the attribute values, the Spring automagical aliasing works only when you use AnnotatedElementUtils instead of AnnotationUtils, as the documentation says:
AnnotatedElementUtils defines the public API for Spring's meta-annotation programming model with support for annotation attribute overrides. If you do not need support for annotation attribute overrides, consider using AnnotationUtils instead.
Example:
final Named namedAnnotation = AnnotatedElementUtils.findMergedAnnotation(Person.class, Named.class);
final String name = namedAnnotation.name();
assertEquals("Steve", name);
Is there a way that I can pass properties to the compound annotation to one of the meta annotations?
I think the simple answer is "no". There is no way to ask Person what annotations it has on it and get #Named for example.
The more complex answer is that you can chain annotations but you would have to investigate these annotations via reflection. For example, the following works:
#Bar
public class Foo {
public static void main(String[] args) {
Annotation[] fooAnnotations = Foo.class.getAnnotations();
assertEquals(1, fooAnnotations.length);
for (Annotation annotation : fooAnnotations) {
Annotation[] annotations =
annotation.annotationType().getAnnotations();
assertEquals(2, annotations.length);
assertEquals(Baz.class, annotations[0].annotationType());
}
}
#Baz
#Retention(RetentionPolicy.RUNTIME)
public #interface Bar {
}
#Retention(RetentionPolicy.RUNTIME)
public #interface Baz {
}
}
However the following statement will return null:
// this always returns null
Baz baz = Foo.class.getAnnotation(Baz.class)
This means that any 3rd party class that is looking for the #Baz annotation won't see it.