Modify parameter value of custom annotation - java

Is there any way to implement annotation in order to change his parameter value by itself?
For example:
I would like create custom RequestMapping annotation to get rid of some code duplicates.
Current code:
#RequestMapping("/this/is/duplicate/few/times/some")
public class SomeController {
}
And I want to create something like this
#Retention(RetentionPolicy.RUNTIME)
#RequestMapping()
public #interface CustomRequestMapping {
String value() default "";
#AliasFor(annotation = RequestMapping.class, attribute = "value")
String "/this/is/duplicate/few/times"+value();
}
In order to reduce Request Mapping value to this:
#CustomRequestMapping("/some")
public class SomeController {
}
Unfortunately I cant find way to make that compilable.
Or maybe there is a way to use AliasFor annotation to pass parameter into destination array. Something like this:
#Retention(RetentionPolicy.RUNTIME)
#RequestMapping()
public #interface CustomRequestMapping {
#AliasFor(annotation = RequestMapping.class, attribute = "value{1}")
String value() default "";
#AliasFor(annotation = RequestMapping.class, attribute = "value{0}")
String prefixPath() default "/this/is/duplicate/few/times";
}

What it seems is you are trying to make a subtype of an annotation and modify one of its attributes default value
Subtyping of annotation is not possible and here is the JSR stating the reason.
It complicates the annotation type system,and makes it much more difficult
to write “Specific Tools”.
“Specific Tools” — Programs that query known annotation types of arbitrary
external programs. Stub generators, for example, fall into this category.
These programs will read annotated classes without loading them into the
virtual machine, but will load annotation interfaces.

One solution to your duplication problem could be to extract a constant.
#Annotation(MyClass.FOO+"localValue")
public class MyClass
{
public static final String FOO = "foo";
...
}

Related

Sharing a "configuration" between annotations

I want to share a common "configuration" between multiple annotated classes. My initial approach was to point the annotations to a class which then extends the configuration class:
#MyAnnotation(config = SharedConfig.class)
class A {}
#MyAnnotation(config = SharedConfig.class)
class B {}
class SharedConfig extends BaseConfig{
public SharedConfig(){
super("abc",123)
}
}
My initial approach was to find the SharedConfig type during annotation processing and instantiate it to find out the actual config. The problem is I can't instantiate the SharedConfig class during the actual processing...
Any idea how to achieve this?
Indeed, you can't instantiate the config class. But what you can do, is query which annotations are on the config class.
So, you could imagine the following system:
old way:
#Movie(
name = "A Few Good Men",
director = #Director(lastName = "Reiner", firstName = "Rob"),
releaseYear = 1992,
liked = true)
public class Foo {}
#Movie(
name = "A Few Good Men",
director = #Director(lastName = "Reiner", firstName = "Rob"),
releaseYear = 1992,
liked = true)
public class Bar {}
new way:
#Movie(
name = "A Few Good Men",
director = #Director(lastName = "Reiner", firstName = "Rob"),
releaseYear = 1992)
public class Placeholder {
// This class serves solely as a place to store the above annotation.
private Placeholder() {}
}
#Movie(
config = Placeholder.class,
liked = true)
public class Foo {}
#Movie(
config = Placeholder.class,
liked = false)
public class Bar {}
Your rule would presumably be that anything explicitly set on the actual class is taken, and if there is nothing explicitly set but there is a 'config' class set, then the value from the annotation on that config class is taken.
Unfortunately, there's no (non-hacky) way to tell the difference between #Foo and #Foo(value="") when Foo is defined as #interface Foo {String value() default "";} - i.e. there is no way to differentiate an explicit setting of a value that is the same as the default value for a given anno parameter, so you can't actually use 'if you do not explicitly set it, then this defaulting mechanism applies' as a concept in annotations. Therefore, 'use the defaulting mechanism' must be based on the actual value - you need 'stand-in' values that mean: "Inherit from config". That means booleans are right out, unfortunately. You can use enums instead.
Here is an example:
public enum LikeStatus {
LIKED, DISLIKED, INHERIT;
}
// target classes/types
public #interface Movie {
Class<?> config() default Object.class;
LikeStatus liked default LikeStatus.INHERIT;
int releaseYear() default 0;
Director director() default #Director(lastName = "", firstName = "")
String name() default "";
}
and now you need to write some code that knows about the defaults and acts accordingly (so, if name() returns an empty string, that means you should check the config class for the Movie annotation and fetch its name. Same for a release year of 0, a director with a blank first and last name, and so on.

Annotation definition with multiple parameters

There are two parameters in the definition, value and optional.
but in the usage, there is only one parameter, so I have two questions:
If there are less parameters in usage than in definition, and the parameter name is absent, how to determine which one is used?
In the above code, value is an array type, but AuthRequired.AuthType.Server is an element, how can they match?
There is an annotation definition:
Java Code:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthRequired {
enum AuthType {
Server(null),
Student(UserType.Student),
Teacher(UserType.Teacher),
SchoolMaster(UserType.SchoolMaster),
Operator(UserType.Operator);
private UserType userType;
AuthType(UserType userType) {
this.userType = userType;
}
public UserType getUserType() {
return userType;
}
}
AuthType[] value() default {};
boolean optional() default false;
}
And its usage:
Java Code:
#AuthRequired(AuthRequired.AuthType.Server)
#RequestMapping(method = RequestMethod.GET, value = "/isUserSlaveOf")
boolean isUserSlaveOf(
#RequestParam String slaveUserIds,
...
...
If you do not specify the parameter name, it's always the value parameter that's used. Also this is only legal, if you specify a single parameter. Otherwise you need to use <parameterName>=<value> for each parameter.
Instead of specifying a single element array using the {} bracket notation you are also allowed to simply write the element. #AuthRequired(value=AuthRequired.AuthType.Server) is equivalent to #AuthRequired(value={AuthRequired.AuthType.Server}).

Getting the original Object form an Annotation object

I am in a situation where I am getting all the annotations of a class using
final Annotation[] annotations = declaringClass.getAnnotations();
Now I know that one of the annotations is of type MyAnnotation which has the following format
public #interface MyAnnotation {
boolean state() default true;
I would like to be able to get the value set for the parameter state, how do I do this? Annotation seems to be a proxy and not the actual object.
If you're just looking for that particular annotation, you can get it directly like this:
MyAnnotation a = declaringClass.getAnnotation(MyAnnotation.class);
boolean state = a.state();
If you specifically want it from your array, just cast it:
MyAnnotation a = (MyAnnotation)annotations[i];
boolean state = a.state();

Java annotation: elements declared as method but value set as attribute

When we create a custom annotation, we declare elements as methods and later set values as if they were attributes.
For example, here we have declared a custom annotation ComponentType with elements name() and description() that look like methods.
public #interface ComponentType {
String name();// declared as method
String description();
}
When the annotation is used, they look like the below:
#ComponentType(name = "userContainer", // value looks like an attribute
description = "a user container")
public class UserEntity { }
My question is: Why doesn't Java allow to declaring elements as attributes, like this?
public #interface ComponentType {
String name; // Compilation Error
String description;
}
If the properties of an annotation weren't defined as abstract methods in an interface, they would have been members. Something like:
public #interface ComponentType {
String name;
String description;
}
However, all the members in an interface are implicitly final (and static) and the above code does not compile, because name and description aren't initialized.
But if they were actually initialized with some values:
public #interface ComponentType {
String name = "name";
String description = "description";
}
then snippets like the following one wouldn't have been possible:
#ComponentType(
name = "userContainer" //cannot assign a value to a final variable
, description = "a user container")
My observation is:
Java consider annotations as special type of interface so:
Like interface we can declare only final attributes in an annotation:
String appName = "test application";//final attribute, never reset value
Annotation may contains only abstract methods(a method that is declared without an implementation).
public #interface ComponentType {
String name();// declared as abstract method
When we annotated elements(e.g. class, method, attribute) by annotation we need to set return value of those abstract methods, which looks like attribute but actually acts as an implementation.
#ComponentType(name = "userContainer"//set return value of method name()
We can use the values we set during annotated elements(e.g. class, method, attribute) by simply calling abstract methods of annotation.
Annotation annotation = annotatedClassObject.getAnnotation(ComponentType.class);
ComponentType componentType = (ComponentType) annotation;
String someName = componentType.name(); //returns value set during annotating
So like as interface,
Annotation never support to declare any non-final attributes.
Annotation may contains some abstract methods and we need to set return value of
abstract method during annotated elements(e.g. class, method,
attribute).
Expecting More Feedback / Answer

Creating custom annotations with place-holder?

I'm creating some custom annotations. I need to create someones with "place-holders" as it is used in Spring
#Value("#{aParameter}")
or in JSF 2
#ManagedProperty(value="#{aParameter}")
I suppose that I must have a mapping somewhere (.properties or .xml file or an enum class) but I need to know to code this approach in custom annotation interface. I mean how to declare a place-holder in the annoatation interface ? and how to ensure the assignement of its value (in mapping file) when applying the annotation somewhere?
Thanks in advance.
You don't do it in the annotation declaration - you do it in the code using that annotation.
For example the #Value is declared like this:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public #interface Value {
/**
* The actual value expression: e.g. "#{systemProperties.myProp}".
*/
String value();
}
and if you trace how it's used you'll see that in org.springframework.web.bind.annotation.support.HandlerMethodInvoker class the value is fetched directly from the annotation defaultValue = ((Value) paramAnn).value(); and then resolved like this:
if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
...
class AnnotationMethodHandlerAdapter{
...
protected Object resolveDefaultValue(String value) {
if (beanFactory == null) {
return value;
}
String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return value;
}
return exprResolver.evaluate(placeholdersResolved, expressionContext);
}
So the logic taking care of resolving properties and such is placed in classes
that actually use read annotations and make them useful.

Categories

Resources