I was trying to go through some online material to learn annotation in java.
In the following code, what happened to my dear "Hello world" string which I passed in this line: #Test_Target(doTestTarget="Hello World !")?
#Target(ElementType.METHOD)
public #interface Test_Target {
public String doTestTarget();
}
above is the annotation defined and below is its usage
public class TestAnnotations {
#Test_Target(doTestTarget="Hello World !")
private String str;
public static void main(String arg[]) {
new TestAnnotations().doTestTarget();
}
public void doTestTarget() {
System.out.printf("Testing Target annotation");
}
}
When I run this code it is only printing Testing Target annotation
Please help me out, I am completely new to annotation.
Annotations are basically bits of data you can attach to fields, methods, classes, etc.
The syntax for declaring annotations in Java is a little awkward. They look a bit like interfaces (they are, after all, declared with #interface), but they aren't really interfaces. I think you might have put the doTestTarget() method in your TestAnnotations class because you thought your annotation was an interface and you needed to implement it. This isn't true - you can delete this method and the call to it from your code if you wish and doing so won't cause you any problems.
Also, you might not have intended to put the annotation on the field str. Annotations apply only to what immediately follows them. As a result, your code doesn't compile, because you've applied your annotation to a field but declared that your annotation can only be applied to methods. Change #Target(ElementType.METHOD) to #Target(ElementType.FIELD) and your code should then compile.
As for what happens to the string Hello World !, it gets written to the .class file and is available to any tool that reads in Java classes. However, it wouldn't necessarily be available in the JVM at runtime. This happens because you didn't specify a #Retention for your #Test_Target annotation. The default value for #Retention is RetentionPolicy.CLASS, which means that the JVM might not bother to load them out of the class file. (See the Javadoc for the RetentionPolicy enum.)
I imagine you want to see some way of reading the value out of this annotation at runtime. If so, I'd recommend adding #Retention(RetentionPolicy.RUNTIME) to your annotation to make sure it will be available at runtime.
To access your annotation and the value contained within it at runtime, you need to use reflection. I've rewritten your TestAnnotations class as follows to give a quick demonstration:
import java.lang.reflect.Field;
public class TestAnnotations {
#Test_Target(doTestTarget="Hello World !")
private String str;
public static void main(String[] args) throws Exception {
// We need to use getDeclaredField here since the field is private.
Field field = TestAnnotations.class.getDeclaredField("str");
Test_Target ann = field.getAnnotation(Test_Target.class);
if (ann != null) {
System.out.println(ann.doTestTarget());
}
}
}
When I run this code, it gives me the following output:
Hello World !
In principle, adding an annotation by itself does not fundamentally alter the programs behaviour.
In your case, you created a new annotation type #Test_Target, which can by used on any method (as indicated by its #Target annotation).
Then you applied this not to a method, but to the str field (which should give a compiler error, I think).
Independently of this, you are creating an object with a doTestTarget method, and invoke it, and get the expected result (i.e. the method is executed).
If you want your annotation to do something more than simply be there and provide some information for the reader of the source, you have to use it - either with an annotation processor at compile time, or using reflection on run time (then you would need also #Retention(RUNTIME) as an annotation on Test_Target.)
In the spirit of learning, another way is to use the annotated class without targeting a method or field.
First declare your interface with the method you need and Retention Policy to Runtime
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
public #interface Test_Target {
public String doTestTarget() default "default string";
}
then annotate the interface created to your class. From your class find the annotated class and then call the method with it.
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
#Test_Target(doTestTarget="Hello World !")
public class TestAnnotations {
public static void main(String[] args) throws Exception
{
AnnotatedElement c = TestAnnotations.class;
if(c.isAnnotationPresent(Test_Target.class))
{
Annotation singleAnnotation = c.getAnnotation(Test_Target.class);
Test_Target tt = (Test_Target) singleAnnotation;
System.out.println(tt.doTestTarget());
}
}
}
the result is:
Hello World !
Related
In the javadoc for java.lang.annotation.RetentionPolicy it says that:
SOURCE: Annotations are to be discarded by the compiler.
CLASS: Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior.
RUNTIME: Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
I understand, that RUNTIME is used to access it using the reflection API, SOURCE for compiler related information (and maybe documentation) and CLASS, as far as I could find out, for special cases, like bytecode manipulation tools, etc. and also compiler related stuff, like #FunctionInterface.
But why is CLASS the default? I expect most annotations to be annotated with RUNTIME, because I think, that most programmers use annotations to specify metadata, that should be read through the reflection API at runtime, because the average programmer doesn't play around with the generated bytecode (at least I've never done it).
So why is RUNTIME not the default? Is there any use case for CLASS, that at I'm not aware of? Or is this just another case of this decision was made long ago, for now unknown or irrelevant reasons and can't be changed, because that would break stuff.
At least for beginners, it may be very confusing and can lead to bugs, that the code
package test;
import java.lang.annotation.Annotation;
import test.Test.Example;
#Example("example")
public class Test {
public static void main(String[] args) {
Annotation[] annotations = Test.class.getAnnotations();
if (annotations.length == 0) {
System.out.println("Class Test has no annotations");
} else {
System.out.println("Class Test has the following annotations:");
for (Annotation annotation : annotations) {
System.out.println("\t" + annotation.toString());
}
}
}
// #Retention(RetentionPolicy.RUNTIME)
public static #interface Example {
public String value();
}
}
outputs "Class Test has no annotations" without the #Retention meta annotation.
I would like to use custom Java annotation to insert a value in a private class property using Spring AOP (and/or AspectJ). Quick example:
MyAnnotation.java:
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {
}
MyController.java:
public class MyControllerImpl implements MyController {
...
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
// imagine this is a REST API that gets called on #GET
// request and returns a string
System.out.println(this.var1); // <-- I'd like this to be "helloworld"
// this is just for illustration
// of course, I will want to do
// something more meaningful with
// the 'var1' variable
return "ok"; <- unimportant for this example
}
...
MyAspect.java:
#Aspect
#Component
public class MyAspect {
#Pointcut("#annotation(com.mypackage.annotation.MyAnnotation)")
public void fieldAnnotatedWithMyAnnotation() {
}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
// problem #1 - the program never enters here
// problem #2 - I need to figure out how to set up the var1 here
// to "helloworld" , how?
return pjp.proceed();
}
...
}
What would I like to happen?
I will call and get into the getVarExample() and after it returns I would like to see "helloworld" in console or log. I would like to somehow set the var1 to a custom value using AOP. Any property variable that will be annotated with #MyAnnotation will be set to "helloworld". I hope the example above is clear.
What have I tried?
I made sure there is no typo in the package names, also fiddled with different AOP advice annotations like #Around and #Before. I also tried different targets in the MyAnnotation and ended up with ElementType.FIELD which should be correct.
Can you help me to get it working?
I know this can be done, but couldn't find any working example online. Again, I would like to see 2 answers:
1. How to get the pointcut to trigger on MyController entrance? I want to catch a breakpoint inside the enrichVar1(..) method of the MyAspect class.
2. How can I modify the annotated var1 value inenrichVar1(..) method of the MyAspect class?
I don't know what I am doing wrong. Any help will be greatly appreciated. Thank you!
The AOP is set up correctly in my project. I know that because I am already using AOP for different things (logging for example).
Update #1:
Please, note there are not getters or setters for the var1 private variable. The variable will be only used within the MyControllerImpl. To illustrate this better I changed the return value of the getVarExample.
Like I said in my comment:
The pointcut designator #annotation() intercepts annotated methods, not annotated fields. For that, native AspectJ has get() and set(). I.e., the pointcut would also need to be changed if migrating to AspectJ. But I agree that sticking to Spring AOP and annotating getter methods instead of fields is probably enough here.
But because you insist that you want to keep the controller class unchanged, here is the native AspectJ solution. Please read chapter Using AspectJ with Spring Applications for how to configure that with #EnableLoadTimeWeaving and JVM parameter -javaagent:/path/to/aspectjweaver.jar.
In order to demonstrate that this solution really does work independently of Spring, I am using no Spring classes or annotations at all, only POJOs and native AspectJ. You can simply do the same within your Spring application. Please note that native AspectJ aspects do not need #Component annotations, in contrast to Spring AOP aspects.
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {}
package de.scrum_master.app;
public interface MyController {
String getVarExample();
}
package de.scrum_master.app;
public class MyControllerImpl implements MyController {
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
System.out.println(this.var1);
return "ok";
}
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
MyController myController = new MyControllerImpl();
myController.getVarExample();
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("get(#de.scrum_master.app.MyAnnotation * *)")
public void fieldAnnotatedWithMyAnnotation() {}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp);
return "helloworld";
}
}
When running Application, the console log is going to be:
get(String de.scrum_master.app.MyControllerImpl.var1)
helloworld
The AspectJ manual explains the syntax of field get and set join point signatures and field patterns.
Note: I think that your use case might be a hack rather than a valid application design. You ought to refactor rather than hack into an application like this.
As it goes from Spring docs Spring AOP does support Spring beans' method execution join points. To make field access join points work you need to use AspectJ's backend with load time weaving for AOP.
But for your case it's not required to use field join points, you can put your annotation on the getter and this should work.
Is it possible that I put custom annotation on class and then it forceful the developer to put the annotation on method?
suppose I have three custom annotation
#Loggable
#MySQLLoggable
#CassandraLoggable
when I put #Loggble annotation on class , it forceful the developer to annotate its all methods either by #MySQLLoggable or #CassandraLoggable.
update me!
EDITED
#Loggable // Suppose I put this annotation on class
public class Service {
#MySQLLoggable //eclipse forceful the developer
//to put #MySQLLoggable or #CassandraLoggable on sayHello()
public String sayHello() {
return null;
}
}
use #interface
public #interface foo {
String str();
}
#foo(str = "str")
public class myClass{
}
edit
public #interface loggable {
#loggable1(method) #loggable2(method))
String foo();
}
Sorry if you want that IDE make developers use your custom annotations then you better read your IDE specification and possible customization. If you want that later on during execution your app will throw exceptions if methods are not annotated then you have to use reflection. Let's save you use Factory Pattern to create objects. So before create them you can check whether all methods are annotated and if not all then throw an exception. Again I believe you have to use reflection.. this is only one way!
I am trying to play with reflection and annotations.
For some reason whenever I add an annotation to a field (or a class or a method), and use reflection to look at the field, I see that its annotations field is null.
For example, this code:
public class Test {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
System.out.println(Test.class.getField("bl").getAnnotations().length);
}
#anno
public int bl;
public #interface anno {}
}
prints 0.
BTW, Java does not ignore annotations in general, and when (for example) I use the #Deprecated annotation - Eclipse recognizes it and tells me the class is deprecated.
I am using Eclipse Indigo and Java SE Development Kit 7 update 2.
Thanks!
By default, annotations are not available to reflection. In order to do so, you need to change the retention policy, i.e.
#Retention(RetentionPolicy.RUNTIME)
public #interface Anno {}
The various retention policies can be found in the Javadoc. The default (for mysterious reasons that nobody seems to know) is CLASS.
I would check the #Retention e.g.
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface Deprecated {
}
If you don't set it, it won't be visible at runtime.
If a class defined an annotation, is it somehow possible to force its subclass to define the same annotation?
For instance, we have a simple class/subclass pair that share the #Author #interface.
What I'd like to do is force each further subclass to define the same #Author annotation, preventing a RuntimeException somewhere down the road.
TestClass.java:
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#interface Author { String name(); }
#Author( name = "foo" )
public abstract class TestClass
{
public static String getInfo( Class<? extends TestClass> c )
{
return c.getAnnotation( Author.class ).name();
}
public static void main( String[] args )
{
System.out.println( "The test class was written by "
+ getInfo( TestClass.class ) );
System.out.println( "The test subclass was written by "
+ getInfo( TestSubClass.class ) );
}
}
TestSubClass.java:
#Author( name = "bar" )
public abstract class TestSubClass extends TestClass {}
I know I can enumerate all annotations at runtime and check for the missing #Author, but I'd really like to do this at compile time, if possible.
You can do that with JSR 269, at compile time.
See : http://today.java.net/pub/a/today/2006/06/29/validate-java-ee-annotations-with-annotation-processors.html#pluggable-annotation-processing-api
Edit 2020-09-20: Link is dead, archived version here : https://web.archive.org/web/20150516080739/http://today.java.net/pub/a/today/2006/06/29/validate-java-ee-annotations-with-annotation-processors.html
I am quite sure that this is impossible to do at compile time.
However, this is an obvious task for a "unit"-test. If you have conventions like this that you would like enforced, but which can be difficult or impossible to check with the compiler, "unit"-tests are a simple way to check them.
Another possibility is to implement a custom rule in a static analyzer. There are many options here, too.
(I put unit in scare-quotes, since this is really a test of conventions, rather than of a specific unit. But it should run together with your unit-tests).
You could make an Annotation (e.g. #EnforceAuthor) with #Inherited on the superclass and use compiler annotations (since Java 1.6) to catch up at compile time. Then you have a reference to the subclass and can check if another Annotation (e.g. #Author)) is missing. This would allow to cancel compiling with an error message.