Main Tutorials

Java Custom Annotations Example

java-custom-annotation

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.

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.

References

  1. Wikipedia : Java annotation
  2. Oracle JavaSE docs – annotations
  3. ElementType JavaDoc
  4. RetentionPolicy JavaDoc

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
32 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Valentin
6 months ago

I love Java!!!

Nicole
6 months ago
Reply to  Valentin

do you? Me too!

public class ILoveJava extends Iterator{
        private String name;
        private int laboca;
}

Valentin
6 months ago
Reply to  Nicole

Good class!! We have so much in common!

Nicole
6 months ago

Yes! Nice

seriously not impressed
1 year ago

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???

Geethanjali
1 year ago

How to pass the json file values using custom annotation in java

cddddddme
3 years ago

But, annotations on vars?

yoshi
3 years ago

Testing

Ranjan
3 years ago

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..

Harish
4 years ago

Question – If custom annotations are placed while writing code, what is the point of having a RetentionPolicy.RUNTIME?

Krishna
4 years ago

It is very Help full thanks .

CR3P
4 years ago

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?

Minh Hoang
4 years ago

Very helpful, thank you!!

Varun Bhatia
5 years ago

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?

Ranjan
3 years ago
Reply to  Varun Bhatia

I also looking same kind of answer Varun.

GetGoing
5 years ago

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.

Rabbit
6 years ago

Thank you

Gopesh
6 years ago

Good one.. Very helpful

Gopesh
6 years ago

Good one..very helpful thanks

Ewen Mackenzie
8 years ago

simply spectacular 😀

Kiran Biliyawala
8 years ago

Simple and Neat .. (Y)

Pradeesh P
8 years ago

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…

Roman
8 years ago

Thanks, good explanation

AZiza Saber
8 years ago

hi friends; how can I read fields , methods ,… of a class by using annotations
thanks
thanks

Jeeva
9 years ago
Murali Mohan
9 years ago

Good one… Annotations demystified largely.

Viacheslav Horobchenko
9 years ago

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

wade
9 years ago

good example simple but clear, thanks

amaresh
9 years ago

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.

denuwanthi
9 years ago

very helpful. thanks

Fr Jeremy
9 years ago

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.

Jan Vladimir Mostert
10 years ago

Can you do the same tutorial using AspectJ ?