How to create a custom annotation in Spring Boot?

Annotation is a powerful feature in Java.

You can plug in some logic by just adding a word !

Consider this annotation in Spring Data:

@Transactional

Add this to a method where you are performing a sequence of database operations and they suddenly turn transactional. If any of the database operation fails all the other executed database operations are rolled back automatically!

You can make use of the same power.

It’s quite easy to create an annotation in spring boot and plugin logic as and where you need it.

Here are the steps to follow:

STEP1: Create an interface with the annotation name

Let’s say we want to trace the requests and responses to a REST method in Spring Boot.

To do this let’s make use of a custom annotation.

Let’s call this

@Traceable

Now , in whichever method you add the above annotation , the method suddenly transforms into a traceable one. The requests and responses are traced automatically!

Let’s say we want to trace the below REST method “test”:

package com.example.demo;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	@PostMapping("/test")
	public String test(@RequestBody Map<String, String> input, HttpServletRequest servletRequest) {

		return "success";
	}
}

We want to print the input , the output and the ip address (which we can get from http servlet request object passed as the second parameter in the above method)

First let’s create the annotation.

Make use of @interface in Java to create the annotation like below:

package com.example.demo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Traceable {

}

Our annotation @Traceable is defined now.

@Target in the above annotation definition defines where to apply the annotation . In our case it is at the method level , so we give ElementType.METHOD as parameter

@Retention denotes when to apply this annotation , in our case it is at run time

Having defined the annotation now let’s build the logic to be applied .

To do this , move to step 2

STEP2: Create an Aspect

The kind of programming we implement using annotations is called Aspect Oriented Programming or declarative programming. You declare in code what to do at certain areas (point cuts is the technical term for this)

To make use of the above annotation , build the logic inside an Aspect class:

package com.example.demo;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TraceableAspect {

	@Around("@annotation(Traceable)")
	public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {

		System.out.println("Input :\n" + joinPoint.getArgs()[0]);

		HttpServletRequest servletRequest = (HttpServletRequest) joinPoint.getArgs()[1];
		
		System.out.println(servletRequest.getRemoteAddr());

		Object result = joinPoint.proceed();

		System.out.println(result);

		return result;
	}

}

Use the annotation @Aspect to let know Spring that this is an Aspect class.

Use the annotation @Component so that Spring will consider this class as a Spring bean.

Create a method with any name and apply the annotation @Around() to define the logic you want to execute .

@Before and @After are other annotations which can be used , they represent the logic to be executed before the start of a method and after the end of a method respectively.

In our case we want to trace both the request and the response , so let’s use @Around

Inside the @Around annotation , pass the annotation name which you created so that Spring knows that it needs to apply the logic around this annotation.

The method should accept a single parameter ProceedingJoinPoint and should return Object data type.

The ProceedingJoinPoint gives access to the method .

From this object you can retrieve the parameters using getArgs() method which returns the input parameters in an array

You can get the input from the first parameter and the ip address from the second parameter as shown above.

You can get the response by calling joinPoint.proceed() . This is where the original method is executed.

Having built the logic , now lets plugin the annotation where we want

STEP3: Add the annotation

Add the annotation on the REST method as below:

package com.example.demo;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	@PostMapping("/test")
	@Traceable
	public String test(@RequestBody Map<String, String> input, HttpServletRequest servletRequest) {

		return "success";
	}
}

That’s it!

We are all set.

Now when you hit the above REST method , the below gets printed:

All because of the @Traceable annotation!

You can add this annotation to whichever REST method you want to trace and the tracing will take place automatically.

Note: you need to use spring-boot-starter-web and spring-boot-starter-aop dependencies for the above to work.

Here is the github link:

https://github.com/vijaysrj/customannotation


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading