What are annotations?
For many first-time developers should have this question? Annontation is a new feature introduced by Java5, and its Chinese name is annotation . It provides a secure annotation-like mechanism for associating any information or metadata with program elements (classes, methods, member variables, etc.). Add more intuitive and clear descriptions to program elements (classes, methods, member variables), which are independent of the program's business logic and used by specified tools or frameworks. Annontation, like a modifier, applies to declarations of packages, types, constructors, methods, member variables, parameters, and local variables.
Java annotations are some meta-information attached to the code, which are used for parsing and use by some tools during compilation and runtime, and have the functions of description and configuration. Annotations do not and cannot affect the actual logic of the code, but only play a supporting role. Included in the java.lang.annotation package.
What is AOP?
It uses a technique called "cross-cutting" to dissect the inside of the encapsulated object, and encapsulate the public behavior that affects multiple classes into a reusable module, which is named "Aspect", that is, the aspect . The so-called "aspects" are simply those that have nothing to do with the business, but are encapsulated by the logic or responsibilities commonly called by the business modules, which is convenient to reduce the repetitive code of the system, reduce the coupling between modules, and is conducive to future operability and maintainability.
AOP related concepts
Aspect: A modularity of concerns whose implementation may additionally cross-cut multiple objects. Transaction management is a good example of a cross-cutting concern in a J2EE application. Aspects are implemented with Spring's Advisor or Interceptor.
Joinpoint: A specific point in the execution of a program, such as a method call or a specific exception being thrown.
Advice: An action performed by the AOP framework at a particular join point. The various types of advice include "around", "before", and "throws" advice. Notification types are discussed below. Many AOP frameworks, including Spring, use the interceptor advice model, maintaining a chain of interceptors "around" the join point. Four advices are defined in Spring: BeforeAdvice, AfterAdvice, ThrowAdvice and DynamicIntroductionAdvice
Pointcut: Specifies a set of join points at which a notification will be raised. AOP frameworks must allow developers to specify pointcuts: for example, using regular expressions. Spring defines the Pointcut interface to combine MethodMatcher and ClassFilter, which can be clearly understood by the name. MethodMatcher is used to check whether the method of the target class can be applied to this notification, and ClassFilter is used to check whether Pointcut should be applied to the target class. superior
Introduction: Add methods or fields to the notified class. Spring allows the introduction of new interfaces to any object being advised. For example, you can use an import to make any object implement the IsModified interface to simplify caching. To use Introduction in Spring, you can implement advice through DelegatingIntroductionInterceptor, and configure the interface to be implemented by Advice and proxy classes through DefaultIntroductionAdvisor
Target Object: The object that contains the connection point. Also known as notified or proxied objects. POJO
AOP Proxy: An object created by the AOP framework that contains notifications. In Spring, AOP proxies can be JDK dynamic proxies or CGLIB proxies.
Weaving: Assembling aspects to create a notified object. This can be done at compile time (e.g. with the AspectJ compiler) or at runtime. Spring, like other pure Java AOP frameworks, does weaving at runtime.
What I want to do today is to use the above two things to print the operation log when each method with a custom annotation is called.
Custom annotations:
Some rules for writing custom annotation classes:
1. Annotation type is defined as @interface, all Annotations will automatically inherit the interface java.lang.Annotation, and can no longer inherit other classes or interfaces.
2. Parameter members only Can be modified with two access rights of public or default (default)
3. Parameter members can only use eight basic data types of basic types byte, short, char, int, long, float, double, boolean and String, Enum, Class, Data types such as annotations, as well as arrays of these types.
4. To obtain annotation information of class methods and fields, you must obtain Annotation objects through Java reflection technology, because you have no other way to obtain annotation objects.
5 . Annotations can also have no defined members, but such annotations are useless
PS: custom annotations need to use meta-annotations
Meta annotations:
Role: responsible for annotating other annotations (can be used for custom annotations)
1、@Retention
Function: Indicate when the annotation is visible (visible at runtime, only visible in .class files and source code, only visible in source code), the available parameters of value are:
attribute value | effect |
---|---|
RetentionPolicy.RUNTIME |
Indicates that the annotation can be found by reflection at runtime (many annotations of the ORM framework use this parameter) |
RetentionPolicy.CLASS |
Indicates that the annotation is stored in the .class file, but cannot be found by reflection at runtime |
RetentionPolicy.SOURSE |
Indicates that the annotation is only visible in the source code |
2、@Target
Function: Indicate what element (class, method, variable, etc.) the annotation is used to annotate. The available parameters of value are:
attribute value | effect |
---|---|
ElementType.PACKAGE |
Indicates that this annotation is used to annotate package declarations |
ElementType.ANNOTATION_TYPE |
Mark this annotation to annotate other annotations |
ElementType.CONSTRUCTOR |
Indicates that this annotation is used to annotate constructors |
ElementType.FIELD |
Indicates that this annotation is used to annotate member variables |
ElementType.METHOD |
Indicates that this annotation is used to annotate methods |
ElementType.TYPE |
Indicates that this annotation is used to annotate classes, interfaces, and enumeration types |
ElementType.PARAMETER |
Indicates that this annotation is used to annotate parameters |
ElementType.LOCAL_VARIABLE |
Indicates that this annotation is used to annotate local variables |
3、@Inherited
Function: Specifies that the annotations modified by it have inheritance (for example, InheritedAnnotation annotates BaseClass, and SubClass inherits BaseClass. At this time, SubClass will also have InheritedAnnotation annotations, but annotations such as methods and parameters will not be inherited)
4、@Documented
Role: Indicates that the annotated element should be added to JavaDoc (documentation)
Let's step into the topic and start coding
1. Annotation definition:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Loggable { /** Operation type (INSERT, UPDATE, SELECT, DELETE) */ public String optType(); /** describe*/ public String describe(); /** Modules */ public String module(); }
2. Define an aspect and define the processing logic when the method returns and when the method reports an error:
@Aspect @Component(value = "loggerAspect") public class LoggerAspect { private Logger logger = LoggerFactory.getLogger(LoggerAspect.class); @Pointcut("execution(public * com.wb.wbao.web.*.*(..)) && @annotation(com.wb.wbao.common.annotation.Loggable)") public void log(){ } @AfterReturning(value = "log()", returning = "retVal") public void log(JoinPoint joinPoint, Object retVal) { // get parameters Object[] params = joinPoint.getArgs(); // get method name String methodName = joinPoint.getSignature().getName(); Class<?> targetClass = joinPoint.getTarget().getClass(); Method method = null; for (Method mt : targetClass.getMethods()) { if (methodName.equals(mt.getName())) { method = mt; break; } } Loggable loggable = method.getAnnotation(Loggable.class); if(Objects.isNull(loggable)){ return; } logger.info("loggable desc:{}, optType:{}, module:{},params:{}", loggable.describe(), loggable.optType(), loggable.module(), params); //loggable desc:登录, optType:POST, module:LOGIN,params:[User{loginName='wangbao', password='wangbao'} } @AfterThrowing(value = "log()", throwing = "ex") public void log(JoinPoint joinPoint, Throwable ex) { // get parameters Object[] params = joinPoint.getArgs(); // get method name String methodName = joinPoint.getSignature().getName(); Class<?> targetClass = joinPoint.getTarget().getClass(); Method method = null; for (Method mt : targetClass.getMethods()) { if (methodName.equals(mt.getName())) { method = mt; break; } } Loggable loggable = method.getAnnotation(Loggable.class); if(Objects.isNull(loggable)){ return; } logger.info("loggable desc:{}, optType:{}, module:{}, exception:{}, params:{}", loggable.describe(), loggable.optType(), loggable.module(), ex.getMessage(), params); //loggable desc:登录, optType:POST, module:LOGIN, exception:/ by zero, params:[User{loginName='wangbao', password='wangbao'} } }
3. Actual use of the annotation:
@Loggable(describe = "登录", optType = "POST", module = "LOGIN") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonDTO login(@RequestBody User user) {...}
Start the project, log in to the system, and see the log if the login is successful:
If the login fails (the called method throws an exception), you see the log: