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
Related
So I created a class with two simple public strings
public final class Right {
private Right() {
super();
}
public static final String AUTH = "hasAuthority('admin') or hasAuthority('mod')";
}
When I used it together with the #PreAuthorize annotation at my controllers it works like a charm. I do not like that it is hardcoded. For this reason I've put the roles in the properties and I tried to use it as a component:
#Component("authRule")
public class AuthRule {
#Value("${role.administrator}")
private String roleAdmin;
#Value("${role.moderator}")
private String roleMod;
public String getRightAccess() {
return "hasAuthority('" + roleAdmin+ "')" + " or hasAuthority('" + roleMod+ "')";
}
}
When i use it in my PreAuthorize as :
#PreAuthorize("#authRule.getRightAccess()")
I am getting back an exception of Failed to convert from type [java.lang.String] to type [java.lang.Boolean] for value 'hasAuthority('admin') or hasAuthority('mod')
if I hardcoded in the PreAuthorize. I am quite confused with this. Anyone any ideas?
Thanks in advance for all the responses.
I've had a quick read of the documentation; https://docs.spring.io/spring-security/site/docs/current/reference/html5/#el-pre-post-annotations
It's likely that the value provided to the annotation is parsed only once. I think you need it to parse twice; once to trigger your custom component's method getRightAccess(). And a second time to parse the String result returned by that method. There are examples here if you do a general search for "Boolean"; https://docs.spring.io/spring/docs/4.3.10.RELEASE/spring-framework-reference/html/expressions.html
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class);
So you probably need something like this in your getRightAccess() method;
return parser.parseExpression("hasAuthority('"+roleAdmin+"') or hasAuthority('"+roleMod+"')").getValue(Boolean.class);
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.
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.
I'm using Hibernate Validator 4.2.0.Final and I'm looking for the simplest way to include class field name in my error message.
What I found is the following thread Using a custom ResourceBundle with Hibernate Validator. According to this I should create my custom annotation for each constraint annotation adding one property to each one.
Is there a cleaner way to achieve this?
The following code:
#Size(max = 5)
private String myField;
produces default error: size must be between 0 and 5.
I would like it to be: myField size must be between 0 and 5.
You can get the name of the field with the getPropertyPath() method from the ConstraintViolation class.
A good default error message can be:
violation.getPropertyPath() + " " + violation.getMessage();
Which will give you "foo may not be null", or "foo.bar may not be null" in the case of nested objects.
If your messages are in .properties file then there is no interpolation variable for accessing property name but one way you can achieve that is
//in ValidationMessages.properties
app.validation.size.msg=size must be between {min} and {max}
#Size(min=10, max=15, message = "myField {app.validation.size.msg})
private String myField;
OR
//in ValidationMessages.properties
app.validation.size.msg=size must be between {min} and {max} but provided ${validatedValue}
#Size(min=10, max=15, message = "myField {app.validation.size.msg})
private String myField;
Reference: message interpolation
I put every field validation message into the properties file, like this:
field.student.name.size.message = Student name size is not valid.
and in the bean, use it like this:
#Size(max = 5, message = "${field.student.name.size.message}")
private String myField;
I know it isn't a perfect solution, but I also don't find a better way.
I am not aware of any generic way but you can define custom error message and include field name in it.
#Size(max = 5, message = "myField size must be between 0 and 5")
private String myField;
A better way so that internationalization is supported.
//in ValidationMessages.properties
app.FieldName=MyField
app.validation.size.msg=size must be between {min} and {max} but provided ${validatedValue}
#Size(min=10, max=15, message = "{app.FieldName}"+" "+ "{app.validation.size.msg}")
private String myField;
use oval this has good number of annotations and possible ways to display messages.
For all those who are looking for a way to access class inside your validator. Putting hibernate annotating on a class level instead of variable level gives you access to a class object (assuming that you have defined a custom validator).
public class myCustomValidator implements ContraintValidator <MyAnnotation, MyAnnotatedClass> {
public void initialize (...){ ... };
public boolean isValid (MyAnnotatedClass myAnnotatedClass) {
// access to elements of your myAnnotatedClass
}
}
use this method(ex is ConstraintViolationException instance):
Set<ConstraintViolation<?>> set = ex.getConstraintViolations();
List<ErrorField> errorFields = new ArrayList<>(set.size());
ErrorField field = null;
for (Iterator<ConstraintViolation<?>> iterator = set.iterator();iterator.hasNext(); ) {
ConstraintViolation<?> next = iterator.next();
System.out.println(((PathImpl)next.getPropertyPath())
.getLeafNode().getName() + " " +next.getMessage());
}
If validating a REST call in a controller and using a controller advice, you can combine field and default message from MethodArgumentNotValidException like this:
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatus status, WebRequest request) {
String errorMessage = exception
.getBindingResult()
.getFieldErrors()
.stream()
.map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
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).