Java Custom Annotations Example
In this tutorial, we will show you how to create two custom annotations – @Test
and @TestInfo
, to simulate a simple unit test framework.
P.S This unit test example is inspired by this official Java annotation article.
1. @Test Annotation
This @interface
tells Java this is a custom annotation. Later, you can annotate it on method level like this @Test(enable=false)
.
Test.java
package com.mkyong.test.core;
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.METHOD) //can use in method only.
public @interface Test {
//should ignore this test?
public boolean enabled() default true;
}
Note
Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types.
Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types.
2. @TesterInfo Annotation
This @TesterInfo
is applied on class level, store the tester details. This shows the different use of return types – enum, array and string.
TesterInfo.java
package com.mkyong.test.core;
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.TYPE) //on class level
public @interface TesterInfo {
public enum Priority {
LOW, MEDIUM, HIGH
}
Priority priority() default Priority.MEDIUM;
String[] tags() default "";
String createdBy() default "Mkyong";
String lastModified() default "03/01/2014";
}
3. Unit Test Example
Create a simple unit test example, and annotated with the new custom annotations – @Test
and @TesterInfo
.
TestExample.java
package com.mkyong.test;
import com.mkyong.test.core.Test;
import com.mkyong.test.core.TesterInfo;
import com.mkyong.test.core.TesterInfo.Priority;
@TesterInfo(
priority = Priority.HIGH,
createdBy = "mkyong.com",
tags = {"sales","test" }
)
public class TestExample {
@Test
void testA() {
if (true)
throw new RuntimeException("This test always failed");
}
@Test(enabled = false)
void testB() {
if (false)
throw new RuntimeException("This test always passed");
}
@Test(enabled = true)
void testC() {
if (10 > 1) {
// do nothing, this test always passed.
}
}
}
4. Java reflection – Read the Annotation
Below example show you how to use Java reflection APIs to read and process the custom annotations.
RunTest.java
package com.mkyong.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import com.mkyong.test.core.Test;
import com.mkyong.test.core.TesterInfo;
public class RunTest {
public static void main(String[] args) throws Exception {
System.out.println("Testing...");
int passed = 0, failed = 0, count = 0, ignore = 0;
Class<TestExample> obj = TestExample.class;
// Process @TesterInfo
if (obj.isAnnotationPresent(TesterInfo.class)) {
Annotation annotation = obj.getAnnotation(TesterInfo.class);
TesterInfo testerInfo = (TesterInfo) annotation;
System.out.printf("%nPriority :%s", testerInfo.priority());
System.out.printf("%nCreatedBy :%s", testerInfo.createdBy());
System.out.printf("%nTags :");
int tagLength = testerInfo.tags().length;
for (String tag : testerInfo.tags()) {
if (tagLength > 1) {
System.out.print(tag + ", ");
} else {
System.out.print(tag);
}
tagLength--;
}
System.out.printf("%nLastModified :%s%n%n", testerInfo.lastModified());
}
// Process @Test
for (Method method : obj.getDeclaredMethods()) {
// if method is annotated with @Test
if (method.isAnnotationPresent(Test.class)) {
Annotation annotation = method.getAnnotation(Test.class);
Test test = (Test) annotation;
// if enabled = true (default)
if (test.enabled()) {
try {
method.invoke(obj.newInstance());
System.out.printf("%s - Test '%s' - passed %n", ++count, method.getName());
passed++;
} catch (Throwable ex) {
System.out.printf("%s - Test '%s' - failed: %s %n", ++count, method.getName(), ex.getCause());
failed++;
}
} else {
System.out.printf("%s - Test '%s' - ignored%n", ++count, method.getName());
ignore++;
}
}
}
System.out.printf("%nResult : Total : %d, Passed: %d, Failed %d, Ignore %d%n", count, passed, failed, ignore);
}
}
Output
Testing...
Priority :HIGH
CreatedBy :mkyong.com
Tags :sales, test
LastModified :03/01/2014
1 - Test 'testA' - failed: java.lang.RuntimeException: This test always failed
2 - Test 'testC' - passed
3 - Test 'testB' - ignored
Result : Total : 3, Passed: 1, Failed 1, Ignore 1
Done.
I love Java!!!
do you? Me too!
Good class!! We have so much in common!
Yes! Nice
So… this is only useful to output some simple stuff via System.out.println()…
can only be run via a whole bunch of reflection code…
doesn’t have any useful purpose…
and would’ve been so much less code using a static utility method inside the code than an annotation?
wouldn’t a better example have been something…. useful? like do something on a RestController to prevent having a ton of extra code inside the multiple controller methods???
How to pass the json file values using custom annotation in java
But, annotations on vars?
Testing
Sir, let’s say i have many classes in different different packages then how can i specify them instead of Class obj = TestExample.class;
// Process @TesterInfo
if (obj.isAnnotationPresent(TesterInfo.class)) {
Please help in this..
Question – If custom annotations are placed while writing code, what is the point of having a RetentionPolicy.RUNTIME?
It is very Help full thanks .
I’m wondering what would be the reason why the order is TestA, TestC, and TestB… What happened in the background which made this order?
Very helpful, thank you!!
Nice article. Suppose I create an annotation @AdditionalInfo(client=”testclient”). Now I want to execute only those @Test methods which have “testclient” set in @AdditionalInfo. Selective execution. Similar to grouping, but I wish to do it through annotations. Can anyone help how can I do that?
I also looking same kind of answer Varun.
Based on the article above and a few that I have read on other sites. I have concerns about the implementation part of annotation. I somehow get the feeling that much of the heavy lifting of scanning the classes that have used the annotation is left to end user. However, I wish to understand how do libraries like Jackson(for JSON parsing) or so are able to extend their annotation and yet we do not have to do any explicit scan of classes or so while using them.
Need your help to implement a library with features that can be extended via annotations.
Thank you
Good one.. Very helpful
Good one..very helpful thanks
simply spectacular 😀
Simple and Neat .. (Y)
I have a doubt can we do the package level annoatation without adding the adpaters and package-info.java in the same package.
Real Qstn is :
I have different packages around 20 packges which has to use this annotations. so is it neccessery to put adpaters as well as package-info.java in each and every package. Is there any alternate way for it? please help…
Thanks, good explanation
hi friends; how can I read fields , methods ,… of a class by using annotations
thanks
thanks
Hi, do you have sample of initialize a object through annotation
http://stackoverflow.com/questions/28539917/how-to-initialize-a-object-using-annotation?noredirect=1#comment45397115_28539917
Good one… Annotations demystified largely.
I created a custom method_annotation that allows me to change the order of parts of text in toString() method.
Is it possible to annotate a method before call it (beyond a class definition)?
Here is an expected code:
System.out.println(“Person: ” + person.@ToStringOrder(non-default-parameter) toString() );
How to annotate person.toString() ?
What is the correct syntax if it possible?
Thanks in advance
good example simple but clear, thanks
is it possible to write a custom annotaion which will set a default value to a java bean variable. for example if there is a list and the getter method should be initialised to a new arraylist through a custom annotation. can some one suggest.
very helpful. thanks
This:
Annotation annotation = obj.getAnnotation(TesterInfo.class);
TesterInfo testerInfo = (TesterInfo) annotation;
…can simply be written as:
TesterInfo testerInfo = obj.getAnnotation(TesterInfo.class);
It is not necessary to assign to an intermediate variable of type “Annotation” and then downcast it at runtime – the Java Generics mechanism already handles the type conversion for you at compile time.
Can you do the same tutorial using AspectJ ?