Get outer class by member annotation - java

I have a class with custom annotation for one of class field:
public class Test {
#CustomAnnotation
private String name;
...
}
I just want to know if it possible to get Class<Test> by this annotation? Can't find any suitable api..
public Class<?> getOuterClass(CustomAnnotation annotation) {
...
}
#CustomAnnotation is declared as #Retention(RetentionPolicy.RUNTIME)

No, annotation does not store any data about where it was declared.
Also annotation can work just like any normal interface, so someone can implement annotation in class an make instances of it that were never used as annotations.
You need either include that information yourself, by adding parameter to annotation and then using it #CustomAnn(Test.class) or when reading annotations just remember and include that information yourself in some other object.

Related

How to decide constraint validator class at runtime?

I have defined an annotation for validation like this:
#Documented
#Constraint(validatedBy = MyValidator.class)
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomValid {
//required methods
}
Now, I want to decide the "validatedBy" class at runtime. Like I have a field in my class:
public class MyClass {
#MyCustomValid
MyObject myObject;
}
How do I pass the ConstraintValidator class at runtime. I have different implementations for different cases.
Annotations are compiled into the code at compile time and they can't change, so you need a hack.
Create a validation class which delegates to another validator. The delegate needs to be created at runtime, using whatever algorithm you design. Note that the code might be used concurrently, so you need a thread-safe initialization.

What's the difference between #Cacheable Method and #Cacheable Class on ehcache

Working with ehcache I noticed that #Cacheable annotation could be used on top of a class decleration or on top of a method decleration, such as;
Cacheable class:
#Cacheable
class CacheableClass{
Long l;
Integer i;
String s;
}
Cacheable method:
class ...
#Cacheable
public List<ToBeCached> getCacheableClassList()
{
...
}
If #Cacheable is on top of a class then you cannot give the name of the chache but if you declare on top of a method you can give the name of the cache declared in the configuration xml. I suppose I miss something since using #Cacheable for class declerations seem obsolute to me.
Using ehcache-spring-annotations :
#com.googlecode.ehcache.annotations.Cacheable(cacheName = "test") if we give this at type level then it gives error saying The annotation #Cacheable is disallowed for this location.
According to documents I ever read Annotation Placement :
On a method.
On an interface or
On a public method on a class
Spring recommends that you only annotate methods of concrete classes with the #Cacheable annotation, as opposed to annotating methods of interfaces.
When using proxies, you should apply the #Cacheable annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the #Cacheable annotation, no error is raised, but the annotated method does not exhibit the configured cachable settings.
Using Spring Cache
If you use #org.springframework.cache.annotation.Cacheable(value="test") where value represents the name of the cache. You can specify this at type &/or method level.
You can try this & tell whether you get error or not :-
#com.googlecode.ehcache.annotations.Cacheable(cacheName = "test")
#org.springframework.cache.annotation.Cacheable(value="")
public class PortalDatabaseAdapterImpl{
#com.googlecode.ehcache.annotations.Cacheable(cacheName="test")
#org.springframework.cache.annotation.Cacheable(value="test")
public List<PageControl> getLoginPage() {}
}
If you didn't get error then I have to update myself.
Using #Cacheable at method level means, the results of the method are cached.
Using #Cacheable at interface level is used to define custom annotations as below,
//Custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#Cacheable(value="books", key="#isbn")
public #interface SlowService {
}
The below code
#Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
can be replaced with
#SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
But I have never seen applying #Cacheable annotation at Class level.

Creating custom annotations

How does annotation work with Java? And how can I create custom annotations like this:
#Entity(keyspace=':')
class Student
{
#Id
#Attribute(value="uid")
Long Id;
#Attribute(value="fname")
String firstname;
#Attribute(value="sname")
String surname;
// Getters and setters
}
Basically, what I need to have is this POJO be serialized like this when persisted:
dao.persist(new Student(0, "john", "smith"));
dao.persist(new Student(1, "katy", "perry"));
Such that, the actual generated/persisted object is a Map<String,String> like this:
uid:0:fname -> john
uid:0:sname -> smith
uid:1:fname -> katy
uid:1:sname -> perry
Any ideas how to implement this?
If you create custom annotations you will have to use Reflection API Example Here to process them.
You can refere How to declare annotation.
Here is how example annotation declaration in java looks like.
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Test { }
Retention and Target are known as meta-annotations.
RetentionPolicy.RUNTIME indicates that you want to retain the annotation at runtime and you can access it at runtime.
ElementType.METHOD indicates that you can declare annotation only on methods similarly you can configure your annotation for class level, member variable level etc.
Each Reflection class has methods to get annotations which are declared.
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
Returns this element's annotation for the specified type if such an annotation is present, else null.
public Annotation[] getDeclaredAnnotations()
Returns all annotations that are directly present on this element. Unlike the other methods in this interface, this method ignores inherited annotations. (Returns an array of length zero if no annotations are directly present on this element.) The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.
You will find these methods present for Field, Method,Class classes.
e.g.To retrieve annotations present on specified class at run time
Annotation[] annos = ob.getClass().getAnnotations();

Guice: class- or instance-level binding annotations

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.

Passing annotation properties to meta-annotations

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.

Categories

Resources