I have been trying to set the logTime attribute in my annotation in the spring xml. I am seeing that this is not as easy as I first thought.
#Component
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecTime {
public boolean logTime() default true;
}
I have tried to use the #Value annotation with the interface with no luck:
I)
#Component
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecTime {
#Value("#{ConfigureAnnotation.doLogging}")
public boolean logTime() default true;
}
and also
II)
#LogExecTime(logTime=#Value("#{ConfigureAnnotation.doLogging}"))
Any ideas how I can do this at xml level or is this not possible with annotation dependency injection?
Yeah -- that's not ever going to work.
#LogExecTime(logTime=#Value("#{ConfigureAnnotation.doLogging}"))
will never even compile. Annotations are not executable code, they're just markers -- extra bit of information that are inserted into the class file whole sale.
You could either put this:
#Value("#{ConfigureAnnotation.doLogging}")
boolean logTime = true;
As a real field on a spring managed bean somewhere, or have change your annotation to be like:
#Component
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecTime {
public String logTime() default "true";
}
and have whatever is processing that annotation at run time also accept a spring EL expression and resolve it appropriately, and your component would look like this:
#LogExecTime(logTime = "#{ConfigureAnnotation.doLogging}")
public class SomeComponent {
// blah blah blah
}
Related
I'm using SpringBoot 2.4.2. And I'm struggling with #AliasFor with custom annotation.
I implemented below custom annotation.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface CustomAnnotation {
#AliasFor("aliasAttribute")
String value() default "";
#AliasFor("value")
String aliasAttribute() "";
}
And using it like this.
#CustomAnnoatation("test")
#Component
public class TestClass() {
// codes here
}
And this test code fails.
#SpringBootTest(classes = TestClass.class)
public class CustomAnnotationTest {
#Autowired
TestClass testClass;
#Test
public void valueTest1() {
Annotation annotation = testClass.getClass().getAnnotation(CustomAnnotation.class);
assertThat(((CustomAnnotation) annotation).value()).isEqualTo(((CustomAnnotation) annotation).aliasAttribute());
}
}
with message
org.opentest4j.AssertionFailedError:
Expecting:
<"">
to be equal to:
<"test">
I don't know why, anyone know?
An annotation is static metadata for a class, field etc. so Spring cannot change anything about it. To make features as #AliasFor possible Spring uses, what they call, synthesized annotations. For those to be used/detected you need to utilize the Spring internals to obtain that synthesized annotation and for the #AliasFor to work. For this use AnnotationUtils.findAnnotation (Spring uses that internally as well).
#AliasFor is a Spring feature so without using the Spring components this won't work.
Your test method is basically the same as
#Test
public void valueTest1() {
Annotation annotation = TestClass.class.getAnnotation(CustomAnnotation.class);
assertThat(((CustomAnnotation) annotation).value()).isEqualTo(((CustomAnnotation) annotation).aliasAttribute());
}
Both this test and your test will fail, because they simply don't use the Spring infrastructure for detecting annotations and apply the features of Spring.
When using AnnotationUtils.findAnnotation the test will pass.
class CustomAnnotationTest {
#Test
void testStandardJava() {
CustomAnnotation annotation = TestClass.class.getAnnotation(CustomAnnotation.class);
assertThat(annotation.value()).isEqualTo(annotation.aliasAttribute());
}
#Test
void testWithSpring() {
CustomAnnotation annotation = AnnotationUtils.findAnnotation(TestClass.class, CustomAnnotation.class);
assertThat(annotation.value()).isEqualTo(annotation.aliasAttribute());
}
}
The testStandardJava will fail, the testWithSpring will pass because it uses the proper mechanisms.
I have a custom Annotation like this -
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ControllerAction {
String value();
}
I have a class that uses this annotation like this -
public class TestController extends AbstractController {
public TestController () {
super();
}
#ControllerAction("add")
public void addCandidate(){
}
}
The super class looks like this -
public abstract class AbstractController {
public AbstractController (){
}
public CustomBean processRequest(ServletAction action, HttpServletRequest request) {
Class<AbstractController > controllerClass = AbstractController.class;
for (Method method : controllerClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(ControllerAction.class)) {
Annotation annotation = (ControllerAction) method.getAnnotation(ControllerAction.class);
if(annotation != null){
if(annotation.value().equals(action.getAction())){
method.invoke(controllerClass.newInstance());
}
}
}
}
return null;
}
}
The processRequest(...) method in AbstractController is called from a servlet directly. The processRequest() method figures out the servlet action, and based on that, it should call the method appropriately.
For example, if the ServletAction.getAction() == 'add', processRequest() should automatically call addCandidate() in TestController. But I am not able to get the value of the Annotation. Somehow annotation.value() is giving a compilation error in eclipse. Eclipse is not showing any method I can use to get the annotation value.
I want to know if there is a way to get value() of the Custom Annotation. I dont want to define my Annotation with anything else other than String value(). I want to know if it is possible to achieve what I want with just String value() in my custom Annotation?
Any help is greatly appreciated. Thanks.
You probably need to change
Annotation annotation = (ControllerAction) method.getAnnotation(ControllerAction.class);
To
ControllerAction annotation = method.getAnnotation(ControllerAction.class);
Otherwise the methods specific to ControllerAction will not be known to the compiler as annotation is of type Annotation
Additionally - as pointed out by Sharon Ben Asher - instead of AbstractController.class you should use getClass() to get the class of the actual implementation at runtime. Given the current code only the methods of AbstractController will be checked but not the ones of implementing classes.
My question is related to Java: Annotated Annotations (and passing values), but not entirely the same, so I thought I'd ask anyway. Especially since there were so few answers to that question.
Say I have written an annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface NestedAnnotation {
public String value();
public Class<?> impl() default Void.class;
}
So if I want to use this, I have to do something like #NestedAnnotation("somevalue"). Now, what if I want to put that annotation inside another one:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#NestedAnnotation("need value here!")
public #interface OuterAnnotation {
public String value();
public Class<?> impl() default Void.class;
}
The NestedAnnotation needs a value, and adding a String (like above) works. But what if I wanted to pass on a value that was received by the OuterAnnotation? Is that possible?
I'm trying to create an annotation that will change the content of the annotated field. So far this is my annotation:
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
String value();
}
And I want to use it like this
public class MyClass {
#MyAnnotation("test")
String myField;
}
And then I want to set the value of myField to "test" at compile time. I just don't know how I can access the annotated field from my annotation processor and if it is even possible to change its content at compile time. This is what my annotation processor looks like right now:
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
annotations.stream().flatMap(a -> roundEnv.getElementsAnnotatedWith(a).stream())
.forEach(e -> {
if (!String.class.getName().equals(((VariableElement) e).asType().toString())) {
out.printMessage(Kind.ERROR
, "#MyAnnotation annotation can only be applied to Strings", e);
}
else {
// what to do here?
}
});
return true;
}
I am new to annotations and a little bit lost so any ideas are highly appreciated.
This should be easy using annotation processing, e.g. https://www.javacodegeeks.com/2015/09/java-annotation-processors.html
I have couple of annotations on a getter as below. I would like to skip all the annotations call on the getter based on a boolean in the same class. Is there any way to do it?
#MyAnnotation1(message = "{someMessage1}")
#MyAnnotation2(message = "{someMessage2}")
public Date getFromDate() {
return fromDate;
}
You can define another annotation as #Ignore on that field if that annotated field is set you can simply ignore your annotation processing.
#Ignore
private boolean value;
You can define your own annotation as
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
/**
* This Annotation should be used on boolean field to set specify whether annotation processing should be ignored for that class
*/
public #interface Ignore {
}
Remember this is custom annotation so you will need to write code to handle it
PS Note: Assuming you are writing custom annotations. And there processing.