Want to learn how to create custom annotations in Java? In this hands-on guide, we’ll show you how to build a real-world role-based access control system using them. Java annotations are a powerful feature that allows developers to add metadata to their code. But did you know you can create your own custom annotations to enforce rules, enhance readability, and clean up business logic? In this tutorial, we’ll create a real-world role-based access control system using custom annotations.
In this tutorial, we will:
- Define a custom annotation
@AccessibleRole
to restrict method access based on user roles (ADMIN
,USER
). - Use Java reflection to dynamically inspect methods and invoke them only if the user’s role matches the required role.
- Demonstrate how this applies in a real-world service-like scenario.
What You Will Learn
- What are annotations in Java?
- How to create custom annotation
- How to use reflection to process annotations at runtime
- Implementing a real world access control use case
-
Types of annotations: method-level, field-level, class-level, parameter-level.
Why Use Custom Annotations?
Custom Annotation are great for following reasons :
- Making your code more readable and maintainable.
- Separating concerns (business logic vs. access control)
- Building lightweight frameworks or utilities.
Type Of Annotations in Java
Java allows annotations at various levels:
1.Method Level Annotations: Used to apply Metadata at methods(We have implemented this type of annotation for our tutorial).
2.Field Level Annotations: Used to tag specific fields for validation, dependency injection, etc.
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface InjectValue { String key(); }
3.Class Level Annotations: Useful for marking entire classes with a certain behavior.
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ServiceRole { String value(); }
4.Parameter Level Annotations: Used to annotate method parameters, commonly seen in frameworks like Spring.
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface NotNull {}
5.Constructor and Local Variable Annotations: Less commonly used, but supported via ElementType.CONSTRUCTOR
and ElementType.LOCAL_VARIABLE
.
How to Create Custom Annotations in Java
We want to restrict method access based on roles like USER
or ADMIN
. Let’s create an annotation called @AccessibleRole
:
package Annotations; 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) public @interface AccessibleRole { String value(); }
Explanations:
@Retention(RetentionPolicy.RUNTIME)
:The annotation is available at runtime via reflection.@Target(ElementType.METHOD)
: It can only be used on methods.String value()
: Accepts a role like “ADMIN” or “USER”.
Annotate Your Service Method
We’ll simulate a service where certain actions can only be performed by specific roles.
package Annotations; public class CustomService { @AccessibleRole("ADMIN") public void changeService(){ System.out.println( "Only Admin can change the service"); } @AccessibleRole("USER") public void useService(){ System.out.println( "User can only use the service"); } public void serviceAvailable(){ System.out.println( "viewing the service available"); } }
What this Means?
- Only admins can change the services.
- Users can only use them.
- Anyone can view the services provided.(no restriction).
Using Reflection with Custom Annotations in Java
Now, let’s implement a class that checks the user’s role at runtime and grants or denies access to methods accordingly.
package Annotations; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Scanner; public class CheckingRole { private static Scanner sc = new Scanner(System.in); public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { System.out.println("Choose how you want to login: "); System.out.println("1. USER \n2.ADMIN"); String currentUser = sc.nextLine(); CustomService service = new CustomService(); for(Method method : service.getClass().getDeclaredMethods()){ if(method.isAnnotationPresent(AccessibleRole.class)){ AccessibleRole role = method.getAnnotation(AccessibleRole.class); String currentRole = role.value(); if(currentRole.equals(currentUser)){ System.out.println("Access granted to : " + method.getName()); method.invoke(service); }else{ System.out.println("Access denied to : " + method.getName()); } }else{ System.out.println("Access granted to (public): " + method.getName()); method.invoke(service); } } } }
Code Explanation:
- We use Java Reflection (
getDeclaredMethods
) to iterate through each method inCustomService
. - For each method, we check if it has the
@AccessibleRole
annotation. - If it does, we extract the required role and compare it with the user’s input.
- If the roles match, the method is invoked using
method.invoke(service)
. - If there’s no annotation, we assume the method is public and allow execution by default.
OUTPUT:
Choose how you want to login: 1. USER 2.ADMIN USER Access granted to (public): serviceAvailable Anyone can view the service available Access denied to : changeService Access granted to : useService User can only use the service
Custom annotations in Java are more than just metadata — they can power real, dynamic behavior. With this access control system, you’ve just taken a major step toward writing clean, declarative, and reusable code.
Checkout other such useful tutorials:
Replicate the Facebook login page using HTML and CSS
Encryption and Decryption Of Data in Java Using AES Algorithm
Read and Write Files in Java Using FileReader and FileWriter