I want to look for a specific annotation of every method in a class and if a method with that annotation is found, i would like to invoke it.
Also, if it's not found in the current class, a check on all inheriting classes should be made.
My problem is that there might be some methods that are protected, private etc. and I would like to ignore those modifiers and to gain access to all methods (i.e even if it's private etc.)
So this is how I invoke (given is the name of the annotation that I'm looking for:
if (m.isAnnotationPresent(Given.class)) {
m.invoke(instObj, intArgument);
}
(And this is how I check the rest of the class hierarchy - if i made a mistake somewhere, enlighten me please:
Class<?> superC = c.getSuperclass();
while (!(superC.equals(Object.class))) {
handleGiven(instObj, superC, methods, currentSentence,
methodArgument);
when handleGiven is a recursive call.
You need to use getDeclaredMethods to get all the methods (public, protected, etc), like this:
public Method findMethodWithAnnotation(Class<?> clazz,
Class<? extends Annotation> annotation) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(annotation)) {
return m;
}
}
return null;
}
And check like this:
Class<?> clazz = ..; //get the class
Method m = null;
do {
m = findMethodWithAnnotation(clazz, DesiredAnnotation.class);
clazz = clazz.getSuperclass();
} while (m == null && clazz != null);
System.out.println(m);
Also make sure that your annotation has the following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
If you need the annotation for fields, check the getDeclaredFields, and similars methods.
You need to make the method accessible, before the invocation
m.setAccessible(true);
If you want a more compact and recursive implementation, you can change to:
public Method findMethodWithAnnotation(Class<?> clazz,
Class<? extends Annotation> annotation) {
if (clazz == Object.class || clazz == null) return null;
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(annotation)) {
return m;
}
}
return findMethodWithAnnotation(clazz.getSuperClass(), annotation);
}
And the use is:
Method m = findMethodWithAnnotation(clazz, DesiredAnnotation.class)
if (m == null) log("Nor the class, or any superclass have the desired annotation")
else {
m.setAccessitble(true);
m.invoke(obj, arguments);
}
Considerations:
this don't cover interfaces, if you need to cover interfaces, check getInterfaces() (this method return the interfaces in the order of declaration).
If a a class A has a override method desiredMethod, with no annotation, and extends a class SuperA, witch has a method desiredMethod, with the desired annotation, this return SuperA#desiredMethod, but when you invoke it, it will invoke in the A class (like a normal invocation)
My problem is that there might be some methods that are protected, private etc. and I would like to ignore those modifiers and to gain access to all methods (i.e even if it's private etc.)
You need to call Method.setAccessible(true) before invoking it, not forgetting to restore its original value afterwards, in a finally block.
Related
#Service
public class TestService{
#DynamicReference
private ITestProvider testProvider;
public void run() {
}
}
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
-->NOT NULL
This code is fine in this case. But when I add #Transactional in method run, then #DynamicReference will lose
#Service
public class TestService{
#DynamicReference
private ITestProvider testProvider;
#Transactional
public void run() {
}
}
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
-->NULL
How can I get field annotation #DynamicReference in cglib proxy class?
this is get Field code:
Object o = this.applicationContext.getBean(beanName);
Class<?> clazz = o.getClass();
for (Field filed : clazz.getDeclaredFields()) {
DynamicReference dynamicRefrence = filed.getAnnotation(DynamicReference.class);
}
From Class.getDeclaredFields():
Returns an array of Field objects reflecting all the fieldsdeclared by the class or interface represented by this Class object. This includes public, protected, default(package) access, and private fields, but excludes inherited fields.
In your case, once you have a subclass-based proxy from cglib, the field will only exist in the superclass. Depending on your use case, you might want to collect all fields up in the inheritance chain that have you custom annotation.
Example code:
Collection<Field> fieldsWithAnnotation = new ArrayList<>();
Class<?> clazz = // your class
while(clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
DynamicReference dynamicRefrence = field.getAnnotation(DynamicReference.class);
if(dynamicRefrence != null)
fieldsWithAnnotation.add(field);
}
clazz = clazz.getSuperclass();
}
EDIT: This approach works to find the annotated field. However, doing field.set(proxyInstance, value) will actually set the field in the proxy. This does not help you, as even though the proxy subclasses, it still uses delegation to forward method calls to a wrapped instance of your actual class. Since your goal is apparently to set the field in this wrapped instance, I would advise you to not use a custom field injection but rather setter injection. Your code would look roughly like this (untested):
// in TestService
private ITestProvider testProvider;
#DynamicReference
public void setTestProvider(ITestProvider testProvider) { ... }
// Getting the method
while(clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
DynamicReference dynamicRefrence = method.getAnnotation(DynamicReference.class);
if(dynamicRefrence != null)
methodsWithAnnotation.add(method);
}
clazz = clazz.getSuperclass();
}
// invoking it
method.invoke(proxyInstance, dependencyInstanceYouWantToSet);
The proxy should delegate the method call to your wrapped instance. Maybe you even want to make the method protected.
The alternative would be getting the callback-field of the proxy and setting the field on that instance, but the approach above seems much cleaner (some might say that magic field injection is evil and you should always use setter/constructor injection for a clean oop approach).
Edit 2: maybe you could also rethink if you want to actually reinvent the DI framework and leverage the underlying existing DI framework functionality. Using #Qualifier or some custom injection resolver comes to mind. See eg this tutorial
I've got one abstract class which is extended by multiple concrete classes.
The abstract class initialization block fetch all the methods of the current class hierarchy searching for particular annotated methods.
ConcreteClass2 > ConcreteClass1 > AbstractClass
OtherConcrete1 > AbstractClass
{
...
final List<Method> methods = new ArrayList<>(16);
Clazz<?> clazz = getClass();
while (clazz != Object.class) {
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
methods.add(method);
}
}
clazz = clazz.getSuperclass();
}
METHODS.put(getClass(), methods);
...
}
These concrete classes are instantiated in a web/application server and are used inside Servlets.
As per se Reflection is not the fastest, and as I do not want to repeat the scan every time a concrete type is instantiated (new), what is the best strategy to cache the annotated Method's.
I'm actually using ConcurrentHashMap as <Class<? extends AbstractClass>, List<Method>>, and In the initialization block I check if the Map already contains an entry for the uppermost concrete type.
However I do not think this is the best way to handle it. And by the way, I cannot have external dependencies.
If you are on Java 8, use computeIfAbsent to avoid recomputing the set of methods with annotations:
private static final ConcurrentMap<Class<?>,List<Method>> METHODS = new ConcurrentHashMap<>();
...
Clazz<?> clazz = getClass();
METHODS.computeIfAbsent(clazz, c -> findMethods(c));
...
private static List<Method> findMethods(Class<?> clazz) {
final List<Method> methods = new ArrayList<>(16);
while (clazz != Object.class) {
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
methods.add(method);
}
}
clazz = clazz.getSuperclass();
}
return methods;
}
This would cache methods per class, and avoid recomputing List<Method> for the same class.
Consider the following class:
#ClassAnnotation1
#ClassAnnotation2
class MyClass {
// ...
#MethodAnnotation1
#MethodAnnotation2
private void myMethod(#ParamAnnotation1 #ParamAnnotation2 int i) {
// ...
}
}
During a reflection phase of my application, I need to analyze various code aspects, given a Method instance.
public void analyze(final Method method) {
// do the analysis...
// for example here, method is an instance of myMethod in MyClass
}
I can easily analyze the parameters' Annotation by doing
for (Parameter p : method.getParameters()) {
if (p.getAnnotation(ParamAnnotation1.class) != null) {
// ...
}
}
and get the results I expect.
The method's Annotation's can easily be processed with
method.getAnnotation(MethodAnnotation1.class)
Unfortunately I fail to get the expected results for the class' Annotation's.
In fact, the call to
method.getClass().getAnnotation(ClassAnnotation1.class)`
returns null, whereas MyClass is clearly annotated by #ClassAnnotation1.
How do I get the MyClass annotations from a Method instance?
You have to use method.getDeclaringClass().getAnnotation(ClassAnnotation1.class)
The fact that method.getParameters() returns the method's parameters, probably mislead you into thinking that method.getClass() returns the class containing your method.
Method::getClass() in fact returns Class<? extends Method>, which is clearly not annotated by #ClassAnnotation1. That's why you got null
I am using Scannotation to scan classfiles and get all classes with annotations present on any element of that class. Using reflection i've been able to find out all annotations on parameters in methods, but i need objects of those annotations so i can later get its parameters (or what do you call it).
this is fraction of my code, which will return annotations i want, but i can't work with them.
public Set<Class> getParametersAnnotatedBy(Class<? extends Annotation> annotation) {
for (String s : annotated) {
//annotated is set containing names of annotated classes
clazz = Class.forName(s);
for (Method m : clazz.getDeclaredMethods()) {
int i = 0;
Class[] params = m.getParameterTypes();
for (Annotation[] ann : m.getParameterAnnotations()) {
for (Annotation a : ann) {
if (annotation.getClass().isInstance(a.getClass())) {
parameters.add(a.getClass());
//here i add annotation to a set
}
}
}
}
}
}
i know i can work with it, if i know the annotation, like this:
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
public String name();
public int count();
}
// ... some code to get annotations
MyAnnotation ann = (MyAnnotation) someAnnotation;
System.out.println(ann.name());
System.out.println(ann.count());
but so far i was not able to do it this way, using reflection... I would very much appreciate any directions, thanks in advance.
PS.: is there any way to get object of parameters like Field for fields, Method for methods etc. ?
You need to use a.annotationType. When you call getClass on an annotation you are actually getting its Proxy Class. To get the real class that it is you need to call annotationType instead of getClass.
if (annotation.getClass() == a.annotationType()) {
parameters.add(a.annotationType());
// here i add annotation to a set
}
I m trying to get the annotation details from super type reference variable using reflection, to make the method accept all sub types. But isAnnotationPresent() returning false. Same with other annotation related methods. If used on the exact type, output is as expected.
I know that annotation info will be available on the Object even I m referring through super type.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface Table {
String name();
}
#Table(name = "some_table")
public class SomeEntity {
public static void main(String[] args) {
System.out.println(SomeEntity.class.isAnnotationPresent(Table.class)); // true
System.out.println(new SomeEntity().getClass().isAnnotationPresent(Table.class)); // true
Class<?> o1 = SomeEntity.class;
System.out.println(o1.getClass().isAnnotationPresent(Table.class)); // false
Class<SomeEntity> o2 = SomeEntity.class;
System.out.println(o2.getClass().isAnnotationPresent(Table.class)); // false
Object o3 = SomeEntity.class;
System.out.println(o3.getClass().isAnnotationPresent(Table.class)); // false
}
}
How to get the annotation info?
You're calling getClass() on a Class<?>, which will give Class<Class>. Now Class itself isn't annotated, which is why you're getting false. I think you want:
// Note no call to o1.getClass()
Class<?> o1 = SomeEntity.class;
System.out.println(o1.isAnnotationPresent(Table.class));
First, see java.lang.annotation.Inherited.
Second, as others pointed out, your code is a bit different from your question.
Third, to answer your question..
I have encountered a similar need many times so I have written a short AnnotationUtil class to do this and some other similar things. Spring framework offers a similar AnnotationUtils class and I suppose dozen other packages today also contain pretty much this piece of code so you don't have to reinvent the wheel.
Anyway this may help you.
public static <T extends Annotation> T getAnnotation(Class<?> clazz, Class<T> annotationType) {
T result = clazz.getAnnotation(annotationType);
if (result == null) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
return getAnnotation(superclass, annotationType);
} else {
return null;
}
} else {
return result;
}
}
The o1.getClass() will give you object of type java.lang.Class, which doesn't have #Table annotation. I suppose you wanted o1.isAnnotationPresent(Table.class).
In your code, o1, o2 and o3 are already the Class<?> objects on which you'll want to call isAnnotationPresent(Class<?>), you shouldn't call getClass() on them before, because at this stage, you'll call isAnnotationPresent(Class<?>)on the Class class itself, and not on your SomeEntity class...