Logging with Spring AOP and custom annotations

 
 

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:



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325362327&siteId=291194637