Java custom annotations are generally used with interceptors or AOP . Using the custom annotations to design your own framework can make the code look very elegant.
What is Annotations ?
Java annotation is an annotation mechanism, introduced by jdk5.0. Classes, methods, variables, parameters and packages in the Java language can be labeled. Unlike the Javadoc, Annotation can obtain annotation content through reflection.
When the compiler generates class files, annotations can be embedded in bytecode. The Java virtual machine can retain the annotation content and obtain the annotation content at run time. Of course, it also supports custom Java annotations.
Java defines a set of annotations. There are 7 in total, 3 are in Java Lang, the remaining 4 knowed as meta annotations are in Java.Lang.annotation.
The annotations that act on the code are:
@Override
checks whether the method is an override method. If it is found that its parent class or the referenced interface does not have this method, a compilation error will be reported.@Deprecated
marks obsolete methods. If this method is used, a compilation warning will be reported.@Suppresswarnings
instructs the compiler to ignore warnings declared in annotations.
Meta annotations act on other annotations are:
@Retention
identifies how the annotation is saved, whether it is only in the code, incorporated into the class file, or can be accessed through reflection at run time.@Documented
marks whether these annotations are included in the user document.@Target
marks which Java member this annotation should be.@Inherited
marks the annotation class to which the annotation is inherited (the default annotation does not inherit from any subclasses)
Common Meta Annotations
There are two kinds of meta-annotations that are more commonly used:
@Target
Describes the range of objects modified by the annotation. The value is defined in java.lang.annotation.ElementType. Commonly used include:- METHOD: used to describe the method
- PACKAGE: used to describe the package
- PARAMETER: used to describe method variables
- TYPE: used to describe a class, interface or enum type
@Retention
Only it is defined as RetentionPolicy.RUNTIME can we get the annotation through reflection.- SOURCE: Valid in source files, ignored during compilation
- CLASS: compiled with the source file in the class file, ignored at runtime
- RUNTIME: Valid at runtime
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
String description();
int length();
}
Used Annotations Example
1. Reflection to get annotations
Continue to use the custom annotation @MyField
above, Get annotations via reflection.
public class MyFieldTest {
// user the custion annotataion
@MyField(description = "username", length = 12)
private String username;
@Test
public void testMyField(){
// get the class Template
Class c = MyFieldTest.class;
// get all fields
for(Field f : c.getDeclaredFields()){
// Determine if this field has the MyField annotation
if(f.isAnnotationPresent(MyField.class)){
MyField annotation = f.getAnnotation(MyField.class);
System.out.println("field:[" + f.getName() + "], description:[" + annotation.description() + "], length:[" + annotation.length() +"]");
}
}
}
}
2. Annotation + interceptor = Login Verification
Next, we use the springboot interceptor to implement such a function.
If @LoginRequired
is added to the method, the user is prompted that the interface needs to be logged in to access,
otherwise, no login is required.
First define a LoginRequired annotation.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {}
Then write two simple interfaces to access sourceA, sourceB resources.
@RestController
public class IndexController {
@GetMapping("/sourceA")
public String sourceA(){
return "you are visiting sourceA";
}
@LoginRequired
@GetMapping("/sourceB")
public String sourceB(){
return "you are visiting sourceB";
}
}
Implement spring’s HandlerInterceptor class to implement an interceptor for login authentication.
public class SourceAccessInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("entering the interceptor");
// get the annotation through reflection
HandlerMethod handlerMethod = (HandlerMethod)handler;
LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
if(loginRequired == null){
return true;
}
// Prompt user to log in
response.setContentType("application/json; charset=utf-8");
response.getWriter().print("the source you are visiting needs logined");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
3. Annotation + AOP = Log Printing
First import the dependencies required by the aspect.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Then define an annotation @MyLog
.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {}
Define a logging aspect class.
// Indicates that this is an aspect class
@Aspect
@Component
public class MyLogAspect {
// PointCut indicates that this is a pointcut, and @annotation indicates
// that this pointcut cuts to an annotation
@Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
public void logPointCut(){};
// around notify
@Around("logPointCut()")
public void logAround(ProceedingJoinPoint joinPoint){
// access methodName
String methodName = joinPoint.getSignature().getName();
// access param
Object[] param = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
for(Object o : param){
sb.append(o + "; ");
}
System.out.println("enter [" + methodName + "] method, parameters are:" + sb.toString());
// continue proceed method
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println(methodName + "method done");
}
}
The IndexController in the second example writes a sourceC for testing, plus our custom annotations:
@MyLog
@GetMapping("/sourceC/{source_name}")
public String sourceC(@PathVariable("source_name") String sourceName){
return "you are visiting sourceC";
}