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

32 comments on “Java Custom Annotations Example

    1. do you? Me too!

      public class ILoveJava extends Iterator{
              private String name;
              private int laboca;
      }
      
      
      Reply
  1. 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???

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

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

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

    Reply
  5. 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?

    Reply
  6. 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?

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

    Reply
  8. 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…

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

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

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

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

    Reply
  13. Can you do the same tutorial using AspectJ ?

    Reply

Leave a Comment

Your email address will not be published. Required fields are marked *