This question is a follow up to a question I found before
java: get all variable names in a class
What I want is to get variables from a class, but instead of getting them all, I want only the variables that have the annotation #isSearchable .
So basically I have 2 questions :
How do I create an annotation ?
How to filter my fields by only this annotation ?
And one more thing , if it is something I'm using frequently is it advisable (I'm guessing reflections should be slow).
Thank you
/** Annotation declaration */
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface isSearchable{
//...
}
#isSearchable
public String anyField = "any value";
checking like:
//use MyClass.class.getDeclaredFields() if you want the fields only for this class.
//.getFields() returns the fields for all the class hierarchy
for(Field field : MyClass.class.getFields()){
isSearchable s = field.getAnnotation(isSearchable.class);
if (s != null) {
//field has the annotation isSearchable
} else {
//field has not the annotation
}
}
Here is an example
class Test {
#IsSearchable
String str1;
String str2;
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface IsSearchable {
}
public static void main(String[] args) throws Exception {
for (Field f : Test.class.getDeclaredFields()) {
if (f.getAnnotation(IsSearchable.class) != null) {
System.out.println(f);
}
}
}
}
prints
java.lang.String Test.str1
How to filter my fields by only this annotation ?
You can get by this simple snippet
Field field = ... //obtain field object
Annotation[] annotations = field.getDeclaredAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof IsSearchable){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
In the above snippet you are basically filtering only IsSearchable annotations.
Regarding your one more thing query
Yes reflection will be slow as discussed here, if its possible to avoid, would advise you to avoid.
Field.getDeclaredAnnotations() gives you the annotations for each field.
To answer your supplementary question, I would normally expect reflection to be slow. Having said that, I perhaps wouldn't worry about optimising until this becomes a problem for you.
Hint: Make sure you're checking the up-to-date Javadoc. Google tends to give me Java 1.4 Javadocs, and annotations didn't exist prior to Java 5.
Related
i am trying to implement the Custom Java Annotation :
and wrote following so far:
#Target({ElementType.FIELD,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MappedField{
boolean isValueRequired() default false;
}
My Requirement is if a property/Method of a Class is isValueRequired=true then it should get the mapped value for this property from database:
For example in my db Salutations are like 01 for MR and 02 for MRS and so on.
And when this annotation is enabled i want property value automatically to be fetched from db:
From other questions i got hint that i need to parse the annotations later something like this:
public static void performAnnotationScanOnClass(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for ( Field field : fields ) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
if ( annotation instanceof MappedField) {
MappedField mappedField= (MappedField) annotation;
// if ( field.get( ... ) == null )
// field.set( ... , value)
}
but my point is where exactly i need to write this code for parsing the annotations.
Any help would be highly appreciated.
Thanks
A suitable place. You could for example have a factory that creates the instances and fills the values from the database. Then you'll just need to be careful to create all the instances through the factory (or you don't need to, but then you must realize that the annotations won't be processed either).
I want to get annotations from annotation, but the weird thing is I can't get single annotation from annotation instance. How do I solve this? I want to get annotations from this annotation instance.
public static void test(Annotation annotation) {
System.out.println("ValidBoolean annotation len:" + ValidBoolean.class.getAnnotations().length);
System.out.println(annotation.getClass().getName() + ":" + annotation.getClass().getAnnotations().length);
if (annotation instanceof ValidBoolean) {
ValidBoolean validBoolean = (ValidBoolean) annotation;
System.out.println("[BOOLEAN]" + validBoolean.getClass().getName() + ":" + validBoolean.getClass().getAnnotations().length);
}
}
print result is:
ValidBoolean annotation len:3
com.sun.proxy.$Proxy28:0
[BOOLEAN]com.sun.proxy.$Proxy28:0
If I understand you correctly, you want to do:
Annotation[] annotationAnnotations = annotation.annotationType().getAnnotations();
Generally, annotationType() works such that
someClass.getAnnotation(someAnnotationClass).annotationType() == someAnnotationClass
So annotation instanceof ValidBoolean also implies annotation.annotationType() == ValidBoolean.class, thus invoking .getAnnotations() on them will lead to the same annotations.
It looks like annotation is a proxy object
validBoolean.getClass().getSuperclass().getAnnotations().length
I believe you can do
TopLevelAnnotation topLevelAnnotation = anything.get.getAnnotation(TopLevelAnnotation.class);
NestedAnnotation nestedAnnotation = topLevelAnnotation.annotationType().getAnnotation(NestedAnnotation.class);
Please check
/**
* Returns the annotation type of this annotation.
* #return the annotation type of this annotation
*/
java.lang.annotation.Annotation#annotationType()
in java.lang.annotation.Annotation
I am using custom annotation with aspectj.
#TestLoggingAnnotation(setMessage = "I want to set value here")
public void get() {
String retString = null;
String message = "DEFAULT";
if (message == "DEFAULT") {
retString = "Default Logging";
} else {
retString = "Custom Logging";
}
}
The above is just simple and sample code. My requirement is that I want to pass the parameter value after resulting from method.
In my case I want set retString value to setMessage in custom parameter.
As of now, annotations can only take compile constants and cant be assigned values at runtime, though their value can be used at runtime using #Retention.
discussion follows here
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TestLoggingAnnotation{
String setMessage ();
}
now use reflection to extract and set method param i doubt we can do this with local varibles.
Maybe title "can annotation get context object?" is not correct, but I don't know how to give it a right and clear one.
I use Spring AOP + Java Annotation to save log, here is my code:
CategoryAction.java :
#ServiceTracker(methodDesp="save category, category name:"+this.category.getName())
public String save() throws Exception
{
this.categoryService.save(this.category);
this.setJsonDataSimply(null);
return "save";
}
TrackAdvice.java :
public Object trackAround(ProceedingJoinPoint point) throws Throwable
{
String log = "success";
ServiceTracker tracker = null;
Method method = null;
AbstractAction action = null;
try
{
Object result = point.proceed();
action = (AbstractAction) point.getTarget();
MethodSignature signature = (MethodSignature) point.getSignature();
method = signature.getMethod();
tracker = method.getAnnotation(ServiceTracker.class);
return result;
}
catch (Exception e)
{
log = e.getMessage();
throw e;
}
finally
{
if (tracker != null)
{
String userId = (String) ActionContext.getContext().getSession().get(Constant.USERID);
if (userId == null)
{
userId = "unknown";
}
TrackLog t = new TrackLog();
t.setWhen(new Date());
t.setUserId(userId);
t.setResult(log);
t.setMethodName(action.getClass().getCanonicalName() + "." + method.getName());
t.setMethodDesp(tracker.methodDesp());
this.trackService.save(t);
}
}
}
ServiceTracker is my own annotation, in my TrackAdvice class, I get the current executing method, if the method has a ServiceTracker annotation, then save the methodDesp in annotation to database.
Now the question is the methodDesp in annotation is dynamic, I want to get this object and retrieve its category property.
It seems that Java Annotation doesn't support this, maybe it supports but I don't know how.
What you can do is use some sort of expression language in the annotation value and then run some interpreter in your advice code. One example using SPEL could look like this:
#ServiceTracker(methodDesp="save category, category name: #{category.name}")
And in your advice code, you can then extract the expression token, make use of a SpelExpression and pass it the target reference as root object (you may want to check what's available out of the box in the SPEL API for supporting your use-case(s)).
It seems that Java Annotation doesn't support this
You are correct - there is no way to do this in pure java.
The reason is that because annotations are static metadata that is wired into classes and defined at compile-time (this start to exist only at run-time, not compile-time).
In other words there is no straightforward way to make methodDesp of some annotated method of some class dynamic, since it's value has to be resolved statically, at compile-time.
However, technically there is a way to do something like you want. What I talk about is using javassist to either manipulate or create your classes (and annotations applied to them) at runtime. But be warned that this is rather hacky way and I generally would not recommend to go there.
I've created simple annotation in Java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Column {
String columnName();
}
and class
public class Table {
#Column(columnName = "id")
private int colId;
#Column(columnName = "name")
private String colName;
private int noAnnotationHere;
public Table(int colId, String colName, int noAnnotationHere) {
this.colId = colId;
this.colName = colName;
this.noAnnotationHere = noAnnotationHere;
}
}
I need to iterate over all fields, that are annotated with Column and get name and value of field and annotation. But I've got problem with getting value of each field, since all of them are of different data type.
Is there anything that would return collection of fields that have certain annotation?
I managed to do it with this code, but I don't think that reflection is good way to solve it.
Table table = new Table(1, "test", 2);
for (Field field : table.getClass().getDeclaredFields()) {
Column col;
// check if field has annotation
if ((col = field.getAnnotation(Column.class)) != null) {
String log = "colname: " + col.columnName() + "\n";
log += "field name: " + field.getName() + "\n\n";
// here i don't know how to get value of field, since all get methods
// are type specific
System.out.println(log);
}
}
Do I have to wrap every field in object, which would implement method like getValue(), or is there some better way around this? Basicly all I need is string representation of each field that is annotated.
edit: yep field.get(table) works, but only for public fields, is there any way how to do this even for private fields? Or do I have to make getter and somehow invoke it?
Every object should has toString() defined. (And you can override this for each class to get a more meaningful representation).
So you where your "// here I don't know" comment is, you could have:
Object value = field.get(table);
// gets the value of this field for the instance 'table'
log += "value: " + value + "\n";
// implicitly uses toString for you
// or will put 'null' if the object is null
Reflection is exactly the way to solve it. Finding out things about types and their members at execution time is pretty much the definition of reflection! The way you've done it looks fine to me.
To find the value of the field, use field.get(table)
Reflection is exactly the way to look at annotations. They are a form of "metadata" attached to the class or method, and Java annotations were designed to be examined that way.
Reflection is one way to process the object (probably the only way if the fields are private and don't have any kind of accessor method). You'll need to look at Field.setAccessible and perhaps Field.getType.
Another approach is to generate another class for enumerating the annotated fields using a compile-time annotation processor. This requires a com.sun API in Java 5, but support is better in the Java 6 JDK (IDEs like Eclipse may require special project configuration).