In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces "how to use custom annotations". In daily operation, I believe many people have doubts about how to use custom annotations. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the doubts about how to use custom annotations! Next, please follow the editor to study!
What is a note?
①, quotes from Wikipedia:
Java annotations, also known as Java annotations, are special syntax metadata that are supported by the JDK5.0 version to add source code.
Classes, methods, variables, parameters, and packages in the Java language can all be annotated. Unlike Javadoc, Java dimensions can obtain the content of dimensions through reflection. Annotations can be embedded in bytecode when the compiler generates class files. The Java virtual machine can retain the annotation content, which can be obtained at run time. Of course, it also supports custom Java annotations.
②, content quoted from the network:
Java annotations are a new feature introduced in JDK5. Annotations (also known as metadata) provide a formal way for us to add information to our code, making it very convenient for us to use the data at a later time.
What is the meta-annotation?
The role of meta-annotations is to be responsible for annotating other notes. Java5.0 defines four standard meta-annotation (meta-annotations) types that are used to provide descriptions of other annotation types.
Standard meta-notes:
@ Target
@ Retention
@ Documented
@ Inherited
Before detailing the meaning of these four metadata, let's take a look at a @ Autowired annotation that is often used in our work, and take a look at it: @ Target, @ Retention, and @ Documented are used in this annotation.
@ Target ({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @ Retention (RetentionPolicy.RUNTIME) @ Documentedpublic @ interface Autowired {boolean required () default true;}
@ Target meta note:
The @ Target annotation is specifically used to define which Java elements a custom annotation can be applied to, indicating the scope of action; the value is defined in java.lang.annotation.ElementType.
Public enum ElementType {/ * * class Declaration of interface (including annotation type) or enumeration * / TYPE, / * * declaration of attribute * / FIELD, / * declaration of method * / METHOD, / * method formal parameter declaration * / PARAMETER, / * constructor declaration * / CONSTRUCTOR, / * local variable declaration * / LOCAL_VARIABLE / * * Annotation type declaration * / ANNOTATION_TYPE, / * * package declaration * / PACKAGE}
You can see the scope of the @ Autowired annotation here:
/ / can be used on constructors, methods, method parameters, attributes, annotation types @ Target ({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@ Retention meta-annotation:
@ Retention annotation, translated as persistence, retention. That is, the lifecycle used to modify custom annotations.
The lifecycle of annotations has three phases:
Java Source File Pha
Compile to the class file phase
Operation phase
These three phases are also defined using the RetentionPolicy enumeration type:
Public enum RetentionPolicy {/ * * Annotations are to be discarded by the compiler. * (comments will be ignored by the compiler) * / SOURCE, / * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. * (comments will be recorded in the class file by the compiler, but will not be retained by the virtual machine at run time, which is a default behavior) * / CLASS, / * * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * (comments will be recorded in the class file by the compiler and retained by the virtual machine at run time, so they can be read through reflection) * @ see java.lang.reflect.AnnotatedElement * / RUNTIME}
Describe these three stages in detail:
①, if defined as RetentionPolicy.SOURCE, will be limited to the Java source file, so this comment will neither participate in compilation nor have any effect at run time. This annotation has the same effect as a comment and can only be seen by people who read the Java file.
②, if defined as RetentionPolicy.CLASS, will be compiled into a Class file, so the compiler can do some processing based on annotations at compile time, but the runtime JVM (Java virtual machine) ignores it and cannot read it at run time
③, if defined as RetentionPolicy.RUNTIME, then this annotation can be loaded into the Class object during the load phase of the runtime. Then during the running phase of the program, you can get this annotation by reflection, and execute different program code segments by determining whether there is the annotation or the value of the attribute in the annotation.
Note: almost all custom annotations in actual development use RetentionPolicy.RUNTIME.
@ DocumentedMeta Annotation:
The @ Documented annotation is used to specify whether custom annotations can be generated into the JavaDoc document along with the defined java file.
@ inherited meta-comment:
The @ Inherited annotation specifies that if a custom annotation is written in the declaration section of the parent class, then the declaration section of the subclass can also automatically have the annotation.
The @ Inherited annotation works only for custom annotations where @ Target is defined as ElementType.TYPE.
Custom annotation implementation:
After understanding the above, let's try to implement a custom annotation:
According to the meta-annotations used in the above custom annotations, we know that:
①, the scope of this annotation, can be used on classes (interfaces, enumerations), methods
②, the life cycle of this annotation, is saved by the compiler in the class file, and is retained by JVM at run time and can be read through reflection
Simple use of custom annotations:
A custom annotation has been created above, so how to use it? The following first describes its simple usage, and then will use it to combine interceptor and AOP section programming for practical application.
Scenario 1: custom annotation + interceptor = wrapper for implementing interface response
Use custom annotations combined with interceptors to elegantly implement wrappers for API interface responses.
Before introducing the way of custom implementation, we first briefly introduce the common implementation, through the comparison of the two, we can more clearly find out who is the most elegant.
Common interface response wrapper:
At present, most projects use the front-end separation method, so the front-end and back-end need to interact through the interface. Currently, the most commonly used data format in interface interaction is json, and the most common response format returned by the back-end to the front-end is as follows:
{# return status code code:integer, # return information description message:string, # return data value data:object}
Enumeration classes are often used in projects to define status codes and messages, as follows:
/ * * @ author [Muzilei] official account * @ Title: ResponseCode * @ Description: use the enum class encapsulated response status code and the corresponding response message * @ date: 7:12:50 * / public enum ResponseCode {SUCCESS (1200, "request succeeded"), ERROR (1400, "request failed"); private Integer code; private String message Private ResponseCode (Integer code, String message) {this.code = code; this.message = message;} public Integer code () {return this.code;} public String message () {return this.message;}}
At the same time, a return response wrapper class will be designed in the project, with the following code:
Import com.alibaba.fastjson.JSONObject;import java.io.Serializable;/** * @ author [Muzi Lei] official account * @ Title: Response * @ Description: encapsulated unified response returns class * @ date: 7:07:13 on August 23, 2019 * / @ SuppressWarnings ("serial") public class Response implements Serializable {/ * * response data * / private T date / * * response status code * / private Integer code; / * response description information * / private String message; public Response (T date, Integer code, String message) {super (); this.date = date; this.code = code; this.message = message;} public T getDate () {return date } public void setDate (T date) {this.date = date;} public Integer getCode () {return code;} public void setCode (Integer code) {this.code = code;} public String getMessage () {return message;} public void setMessage (String message) {this.message = message } @ Override public String toString () {return JSONObject.toJSONString (this);}}
Finally, you use the response wrapper class and the status code enumeration class to implement the wrapper that returns the response:
@ GetMapping ("/ user/findAllUser") public Response findAllUser () {logger.info ("start querying all data."); List findAllUser = new ArrayList (); findAllUser.add (new User ("Mu Zilei", 26)); findAllUser.add (new User ("official account", 28)); / / return response to wrap Response response = new Response (findAllUser, ResponseCode.SUCCESS.code (), ResponseCode.SUCCESS.message ()) Logger.info ("response: {}\ n", response.toString ()); return response;}
Enter the URL: http://127.0.0.1:8080/v1/api/user/findAllUser in the browser and click enter to get the following data:
{"code": 1200, "date": [{"age": 26, "name": "Muzilei"}, {"age": 28, "name": "official account"}], "message": "request successful"}
Can we find any problems by looking at the way the response wrapper is implemented?
Answer: the code is redundant and requires a response wrapper in each interface method; so that the interface method contains a lot of non-business logic code
Is there a version to optimize it? En en is thinking. Ah, custom annotations + interceptors can be implemented!
Custom annotations implement the interface response wrapper:
①, first create a custom annotation for the response wrapper:
/ * * @ author [Muzilei] official account * @ PACKAGE_NAME: com.lyl.annotation * @ ClassName: ResponseResult * @ Description: custom note for marking the returned value of the method needs to be wrapped * @ Date: 2020-11-10 10:38 * / @ Target ({ElementType.TYPE, ElementType.METHOD}) @ Retention (RetentionPolicy.RUNTIME) @ Documentedpublic @ interface ResponseResult {}
②, create an interceptor to intercept the request to see if custom annotations are used on the method or class of the request:
/ * * @ author [Muzilei] official account * @ PACKAGE_NAME: com.lyl.interceptor * @ ClassName: ResponseResultInterceptor * @ Description: interceptor: intercepts the request, determines whether the custom @ ResponseResult annotation is used on the method or class of the request, and sets whether the flag bit attribute of the custom annotation is used in the request * @ Date: 2020-11-10 10:50 * * / @ Componentpublic class ResponseResultInterceptor implements HandlerInterceptor {/ * * tag bit. Custom annotation is used on the requested controller class or method. The returned data needs to be packaged * / public static final String RESPONSE_ANNOTATION = "RESPONSE_ANNOTATION". / * request preprocessing to determine whether the custom annotation * / @ Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {/ / request interface method if (handler instanceof HandlerMethod) {final HandlerMethod handlerMethod = (HandlerMethod) handler; final Class clazz = handlerMethod.getBeanType () Final Method method = handlerMethod.getMethod (); / / determine whether the class object has been annotated if (clazz.isAnnotationPresent (ResponseResult.class)) {/ / set the attribute flag that needs to be wrapped for response in the request, and deal with request.setAttribute (RESPONSE_ANNOTATION, clazz.getAnnotation (ResponseResult.class)) in the following ResponseBodyAdvice enhancements } else if (method.isAnnotationPresent (ResponseResult.class)) {/ / set the attribute flag that needs to be wrapped in the response in the request, and handle request.setAttribute (RESPONSE_ANNOTATION, method.getAnnotation (ResponseResult.class));}} return true;} in the following ResponseBodyAdvice enhancements
③, create an enhanced Controller to implement the enhanced processing of wrapping the return response:
/ * * @ author [Muzilei] official account * @ PACKAGE_NAME: com.lyl.interceptor * @ ClassName: ResponseResultHandler * @ Description: enhanced processing for wrapping the return response * @ Date: 2020-11-10 13:49 * * / @ ControllerAdvicepublic class ResponseResultHandler implements ResponseBodyAdvice {private final Logger logger = LoggerFactory.getLogger (this.getClass ()) / * tag bit. Custom annotation is used on the requested controller class or method. The returned data needs to be packaged * / public static final String RESPONSE_ANNOTATION = "RESPONSE_ANNOTATION". Whether the request contains tags that need to be wrapped in the response. If not, return directly without rewriting the return body * * @ param methodParameter * @ param aClass * @ return * / @ Override public boolean supports (MethodParameter methodParameter, Class > aClass) {ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes (); HttpServletRequest sr = (HttpServletRequest) ra.getRequest () / / query whether the response wrapper flag ResponseResult responseResult = (ResponseResult) sr.getAttribute (RESPONSE_ANNOTATION); return responseResult = = null is required? False: true;} / * package the responder In addition, the response body can be uniformly encrypted, signed and other * * @ param responseBody request interface methods are executed to get the return value (response) * / @ Override public Object beforeBodyWrite (Object responseBody, MethodParameter methodParameter, MediaType mediaType, Class > aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {logger.info ("return response wrapper in progress.") ; Response response; / / boolean type to judge whether some database additions, updates and deletions are successful if (responseBody instanceof Boolean) {if ((Boolean) responseBody) {response = new Response (responseBody, ResponseCode.SUCCESS.code (), ResponseCode.SUCCESS.message ()) } else {response = new Response (responseBody, ResponseCode.ERROR.code (), ResponseCode.ERROR.message ());}} else {/ / judgment is like querying some returned data, but no data is returned and null is returned. If (null! = responseBody) {response = new Response (responseBody, ResponseCode.SUCCESS.code (), ResponseCode.SUCCESS.message ());} else {response = new Response (responseBody, ResponseCode.ERROR.code (), ResponseCode.ERROR.message ());}} return response;}}
④, and finally use our custom annotations in Controller; use @ ResponseResult custom annotations on Controller classes or methods; enter URL: http://127.0.0.1:8080/v1/api/user/findAllUserByAnnotation in the browser to view:
/ / Custom annotations are used on methods @ ResponseResult@GetMapping ("/ user/findAllUserByAnnotation") public List findAllUserByAnnotation () {logger.info ("start querying all data..."); List findAllUser = new ArrayList (); findAllUser.add (new User ("Mu Zi Lei", 26); findAllUser.add ("official account", 28)) Logger.info ("wrapper for responses with @ ResponseResult custom annotations to make controller code more brief"); return findAllUser;}
At this point, our interface returns the response wrapper custom annotation implementation to see if the code is concise and elegant.
Summary: in this paper, this scheme is only a simple implementation, if interested friends can be better optimized.
Scenario 2: custom annotations + AOP = elegant use of distributed locks
The most common process for using distributed locks is:
You can get a message from the above code: if there are many methods that need to use distributed locks, then there must be code in each method to acquire and release distributed locks, which will lead to code redundancy.
Is there any good solution? Custom annotations make the code more concise and elegant
Custom annotations elegantly use distributed locks:
①, first implement a custom annotation that marks the use of distributed locks:
/ * * @ author [Muzilei] official account * @ PACKAGE_NAME: com.lyl.annotation * @ ClassName: GetDistributedLock * @ Description: get redis distributed lock annotations * @ Date: 2020-11-10 16:24 * * / @ Target (ElementType.METHOD) @ Retention (RetentionPolicy.RUNTIME) @ Documentedpublic @ interface GetDistributedLock {/ / distributed lock key String lockKey (); / / distributed lock value, default is lockValue String lockValue () default "lockValue" / / Expiration time. Default is 300s int expireTime () default 300;}
②, define a section, and make a surround enhancement notification of the method using @ GetDistributedLock custom annotation in the section:
/ * * @ author: [Muzilei] official account * @ PACKAGE_NAME: com.lyl.aop * @ ClassName: DistributedLockAspect * @ Description: custom annotations combined with AOP section programming elegant use of distributed locks * @ Date: 2020-11-10 16:52 * * / @ Component@Aspectpublic class DistributedLockAspect {private final Logger logger = LoggerFactory.getLogger (this.getClass ()); @ Autowired RedisService redisService / * * Around surround Enhancement Notification * * @ param joinPoint connection point, all methods belong to connection point But when the @ GetDistributedLock custom annotation is used on some methods, * it turns the join point into a pointcut; then weaves additional enhancements on the pointcut; the pointcut and its corresponding enhancement make up the tangent Aspect. * / @ Around (value = "@ annotation (com.lyl.annotation.GetDistributedLock)") public Boolean handlerDistributedLock (ProceedingJoinPoint joinPoint) {/ / get the custom annotation object GetDistributedLock getDistributedLock = ((MethodSignature) joinPoint.getSignature ()) .getMethod (). GetAnnotation (GetDistributedLock.class); / / get the attribute value String lockKey = getDistributedLock.lockKey () in the custom annotation object String LockValue = getDistributedLock.lockValue (); int expireTime = getDistributedLock.expireTime (); if (redisService.tryGetDistributedLock (lockKey, LockValue, expireTime)) {/ / after acquiring the distributed lock, continue to execute the business logic try {return (boolean) joinPoint.proceed () } catch (Throwable throwable) {logger.error ("Business logic execution failed." , throwable);} finally {/ / finally guarantee the release of distributed locks redisService.releaseDistributedLock (lockKey, LockValue);}} return false;}}
③, finally, use @ GetDistributedLock custom annotations on methods in Controller; when custom annotations are used on a method, then the method is equivalent to a pointcut, so the method will be enhanced around (before and after method execution)
Enter the URL: http://127.0.0.1:8080/v1/api/user/getDistributedLock into the browser to trigger the method execution after entering enter:
/ / use @ GetDistributedLock (lockKey = "userLock") @ GetMapping ("/ user/getDistributedLock") public boolean getUserDistributedLock () {logger.info ("acquire distributed locks...") for custom annotations; / / write specific business logic return true;}
By customizing the annotations, you can see that the code becomes more concise and elegant.
Scenario 3: custom annotations + AOP = to print logs
Take a look at the most common ways to print logs, and then talk about how custom annotations can elegantly print logs.
How to print ordinary logs:
By looking at the code above, you can see that if each method needs to print a log, there will be a lot of redundant code.
Custom annotations for log printing:
①, first create a custom comment for marking log printing:
/ * * @ Author: [Muzilei] official account * @ PACKAGE_NAME: com.lyl.annotation * @ ClassName: PrintLog * @ Description: custom annotations for log printing * @ Date: 2020-11-10 18:05 * * / @ Target (ElementType.METHOD) @ Retention (RetentionPolicy.RUNTIME) @ Documentedpublic @ interface PrintLog {}
②, define a section, and make a surround enhancement notification of the method using @ PrintLog custom annotation in the section:
/ * * @ author: [Muzilei] official account * @ PACKAGE_NAME: com.lyl.aop * @ ClassName: PrintLogAspect * @ Description: custom annotations combined with AOP section programming to achieve elegant log printing * @ Date: 2020-11-10 18:11 * / @ Component@Aspectpublic class PrintLogAspect {private final Logger logger = LoggerFactory.getLogger (this.getClass ()) / * * Around surround Enhancement Notification * * @ param joinPoint connection point, all methods belong to connection point But when the @ PrintLog custom annotation is used on some methods, * it turns the join point into a pointcut; then weaves additional enhancements on the pointcut; the pointcut and its corresponding enhancement make up the tangent Aspect. * / @ Around (value = "@ annotation (com.lyl.annotation.PrintLog)") public Object handlerPrintLog (ProceedingJoinPoint joinPoint) {/ / get the name of the method String methodName = joinPoint.getSignature () .getName (); / / get the method input parameter Object [] param = joinPoint.getArgs (); StringBuilder sb = new StringBuilder (); for (Object o: param) {sb.append (o + " ");} logger.info (" enter the "{}" method, the parameters are: {} ", methodName, sb.toString (); Object object = null; / / continue to execute the method try {object = joinPoint.proceed ();} catch (Throwable throwable) {logger.error (" print log processing error. " , throwable);} logger.info ("{} method execution ends." , methodName); return object;}}
③, finally, use @ PrintLog custom annotations on methods in Controller; when custom annotations are used on a method, then the method is equivalent to a pointcut, so the method will be enhanced around (before and after method execution)
@ PrintLog@GetMapping (value = "/ user/findUserNameById/ {id}", produces = "application/json;charset=utf-8") public String findUserNameById (@ PathVariable ("id") int id) {/ / simulate query user name String userName = "Muzilei official account" according to id; return userName;}
④, enter the URL: http://127.0.0.1:8080/v1/api/user/findUserNameById/66 in the browser and trigger the method execution after entering enter. It is found that the log has been printed in the console:
Enter the "findUserNameById" method with the parameter: 66; the execution of the findUserNameById method ends. At this point, the study on "how to use custom annotations" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.