I have a annotation that does include several other annotations, pretty much like this one here:
#Component // Spring Component
#Interface OsgiService { boolean isFactory() }
meaning that all classes annotated with #OsgiService shall automatically also be annotated as #Component. Which works fine.
Now however, I'd like to add another annotation, that has a parameter which is dependent of the isFactory parameter of #OsgiService.
#Component // Spring Component
#Scope(isFactory() ? "prototype" : "singleton")
#Interface OsgiService { boolean isFactory() }
Which does not work. However, as isFactory property of an annotation requires to be a static value, shouldn't it be possible to have something like this?
I don't think this is possible.
You can create two annotations: #OsgiService and #OsgiServiceFactory
Related
With Spring, you can have some kind of composed annotations. A prominent example is the #SpringBootApplication-annotation, which is a composite of an #Configuration, #EnableAutoConfiguration and #ComponentScan.
I am trying to get all Beans that are affected by a certain annotation, i.e. ComponentScan.
Following this answer, I am using the following code:
for (T o : applicationContext.getBeansWithAnnotation(ComponentScan.class).values()) {
ComponentScan ann = (ComponentScan) o.getClass().getAnnotation(ComponentScan.class);
...
}
which does not work, since not all beans, returned by getBeansWithAnnotation(ComponentScan.class) are indeed annotated with that annotation, since those that are e.g. annotated with #SpringBootApplication are (usually) not.
Now I am looking for some kind of generic way, to retrieve the value of an annotation, even when it is only added as piece of another annotation.
How can I do this?
It turns out, there is a utility set AnnotatedElementUtils which allows you to handle those merged annotations.
for (Object annotated : context.getBeansWithAnnotation(ComponentScan.class).values()) {
Class clazz = ClassUtils.getUserClass(annotated) // thank you jin!
ComponentScan mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(clazz, ComponentScan.class);
if (mergedAnnotation != null) { // For some reasons, this might still be null.
// TODO: useful stuff.
}
}
it may be CglibProxy. so you can not directly get the Annotation.
ClassUtils.isCglibProxyClass(o)
for more see this
edit,you can add your logic code. find the ComponentScan.
if (ClassUtils.isCglibProxyClass(o.getClass())) {
Annotation[] annotations = ClassUtils.getUserClass(o).getAnnotations();
for (Annotation annotation : annotations) {
ComponentScan annotation1 = annotation.annotationType().getAnnotation(ComponentScan.class);
// in my test code , ComponentScan can get here.for #SpringBootApplication
System.out.println(annotation1);
}
}
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.
Guice provides two variations of so-called binding annotations, which seem to really break down to class- and instance-level annotations:
"Class-level":
bind(Service.class).annotatedWith(Red.class).to(RedServiceImpl.class);
#Red
public class SomeService implements Service { ... }
Service redSvc = injector.getInstance(SomeService.class);
"Instance-level":
bind(Service.class).annotatedWith(Names.named("Blue").to(BlueServiceImpl.class);
#Blue blueSvc = injector.getInstance(Service.class);
When is one method preferential over the other? It seems that class-level annotations are more absolute/inflexible than instance-level. Pros/cons/caveats/pitfalls of either method?
I'm not sure I understand your question. Your use of binding annotations is irregular. You wouldn't typically annotate a local variable or a class, but rather fields and parameters.
Your first code example will cause the injector to return SomeService, but not because of your annotation or your binding, but because SomeService is a concrete implementation. Had you asked for this instead:
Service redSvc = injector.getInstance(Service.class);
You will get an error:
1) No implementation for com.example.Service was bound.
while locating com.example.Service
Your second example is also incorrect. If you use Names to define a binding, you must use #Named to access that binding. Using #Blue would cause a compiler error. The correct usage would be #Named(value="Blue").
The common best practice for a binding annotation is this:
#BindingAnnotation
#Target({ FIELD, PARAMETER, METHOD })
#Retention(RUNTIME)
public #interface MyAnno
In that case, both of these would be compile errors:
#Red // not allowed
public class SomeService implements Service { ... }
#Blue // not allowed
blueSvc = injector.getInstance(Service.class);
The only real difference is that in one case you bind for a whole annotation, and in the other case you bind to an annotation with specific arguments. Not all annotations even take arguments, in which case, binding with the annotation class is perfectly normal.
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.
I've a problem with spring. I'm replacing the xml file based configuration of spring with annotation based configuration. Because of that I runned into the following problem. Theres is a class, where one field is configured by class based autowiring. Since yet, there has been only one candidate. But now there is more then one candidate, because I've added the #Named tag to mutliple classes of the same interface.
Here is a code example:
The class with the autowiring field:
public class AutowiringClass<X, Y> {
// This is the field which is autowired
private Autowired<X, Y> bean;
.....
}
Then there is a second class, which extends AutoWiringClass with specific generic arguments:
public class TestClass extends AutoWiringClass<ObjectX, ObjectY> {
.....
}
The Problem is, that spring doesn't know which class it should use, since there are more than once class of the type Autowiring but with different generic types (ObjectX, ObjectY). I thought, that the different generic types will do the thing.. but they doesn't :(
It would be awesome if anybody has an solution for that.
Qualifiers are what you are looking for. When you register a class as a Spring bean (using #Component or the like) you can pass a name as an argument. Then when you are autowiring a property, add a #Qualifier annotation with that bean's name. So:
#Component("Test1")
public class Test { }
#Component("Test2")
public class BetterTest extends Test {}
public class TestUser {
#Autowired
#Qualifier("Test1")
private Test test;
}
Generics won't help you due to type erasure, Spring can't do nothing about it. But you can either use #Primary annotation to denote the default implementation that should be always used or switch to autowiring by name rather than by type. Then Spring will use field name when in doubt.