Spring Boot + Custom Annotation
This tutorial will demonstrate how to implement custom annotations at the Method Level in a Spring Boot application.
You can also refer the example for Spring Boot + Field and Class Level Custom Annotation from here.
Spring Boot + Method Level Custom Annotation Example
In this example, will authorize users and determine whether the requested user is valid user or not by using custom annotation.
Now create Spring Boot application from Spring Initializr.
Project Structure
Maven Dependency
Add spring-boot-starter-aop
to create custom annotation using Aspect.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.techgeeknext</groupId>
<artifactId>SpringBootCustomAnnotation</artifactId>
<version>1.0.0</version>
<name>SpringBootCustomAnnotation</name>
<description>Spring Project + Custom Annotation</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Create the annotation
Let us create the annotation in Java by using @interface.package com.techgeeknext.custom.annotation;
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 AuthorizeUser {
}
AuthorizeUser
We have given our custom annotation name asAuthorizeUser
.@Target
It specifies where the annotation should be applied. Because it is at the method level in our case, we passElementType.METHOD
as a parameter.@Retention
It specifies when to use this annotation, which in our case is at run time.
Take a look at our suggested posts:
Create an Aspect
Create Aspect class to build our custom annotation logic.
Aspect Oriented Programming, or declarative programming, is the type of programming that we use for annotations to implement.
In this example, you can authorize users and determine whether the user is valid user or not using
@AuthorizeUser
.
So, before invoking a method, you must perform an authorization validation, if user is authorized, the method is invoked; otherwise, invalid user message will be returned.
- Before calling a method, the user must be authorised.
- Invoke method (joinPoint.proceed();) if User is Authorized/Valid.
package com.techgeeknext.custom.annotation;
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 AuthorizeUserAspect {
@Around("@annotation(AuthorizeUser)")
public Object authorize(ProceedingJoinPoint joinPoint) throws Throwable {
//BEFORE METHOD EXECUTION
Integer user_id = (Integer) joinPoint.getArgs()[0];
System.out.println("User Id: " + user_id);
//Only user id 33 is authorize to login, other user are not valid users.
if (user_id != 33) {
//write authorization verification business logic
System.out.println("Invalid User : " + user_id);
return user_id + " is Invalid User. Please login with correct credential.";
}
//This is where ACTUAL METHOD will get invoke
Object result = joinPoint.proceed();
// AFTER METHOD EXECUTION
System.out.println(result);
return result;
}
}
@Aspect
It specifies that class is an Aspect Class.@Component
It specifies that the class is a Spring bean.authorize method
Create method with any name.Around() with @annotation
Pass the annotation name (that you created using Interface) inside the @Around annotation, so that Spring understands that the logic around this annotation needs to be applied.ProceedingJoinPoint Parameter
The method is accessed via the ProceedingJoinPoint. You can access input parameters received from REST Method from ProceedingJoinPoint.joinPoint.proceed()
This is where the original method is put into action.
Add the annotation
Create a rest controller endpoint to pass the input value (user_id) to validate.
package com.techgeeknext.controller;
import javax.servlet.http.HttpServletRequest;
import com.techgeeknext.custom.annotation.AuthorizeUser;
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController {
/**
* Method to validate if user is valid or not by using AuthorizeUser annotation.
* @param user_id
* @param servletRequest
* @return
*/
@GetMapping("/login")
@AuthorizeUser
public String login(@RequestParam Integer user_id, HttpServletRequest servletRequest) {
return "Hello TechGeekNextUser : " + user_id;
}
}
Test Custom Annotation
- When you start the Spring Boot application and hit the below REST endpoints it'll execute
annotation logic and display the output in console.
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.1) 14:01:22.070 INFO --- [ main] c.t.SpringBootCustomAnnotation : Starting SpringBootCustomAnnotation using Java 12.0.2 on (D:\SpringBootCustomAnnotation\target\classes started in D:\SpringBootCustomAnnotation) 14:01:22.072 INFO --- [ main] c.t.SpringBootCustomAnnotation : No active profile set, falling back to default profiles: default 14:01:23.140 INFO --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 14:01:23.148 INFO --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 14:01:23.148 INFO --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.46] 14:01:23.229 INFO --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 14:01:23.230 INFO --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1120 ms 14:01:23.541 INFO --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 14:01:23.549 INFO --- [ main] c.t.SpringBootCustomAnnotation : Started SpringBootCustomAnnotation in 1.801 seconds (JVM running for 2.071) 14:01:29.294 INFO --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 14:01:29.294 INFO --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 14:01:29.296 INFO --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms User Id: 21 Invalid User : 21 User Id: 33 Hello TechGeekNextUser : 33
- Check with Invalid User: Call the REST endpoint http://localhost:8080/login?user_id=21, with invalid user id to check the response.
- Check with valid User: Call the REST endpoint http://localhost:8080/login?user_id=33, with valid user id to check the response.
In the same way, you can add this annotation to any REST API or any method to make the annotation process happen automatically. For Example, you can use for logging or monitoring purpose.
Download Source Code
The full source code for this article can be found on below.Download it here - Spring Boot + Method Level Custom Annotation Example