SpringBoot integrates AOP aspects and simple understanding of entry point expressions

After reading the text, let’s understand this expression EZ

Read the following article first and then look at this part

First, let me talk about the entry point expression, and then you are going to read the following article

@Pointcut("execution(public * com.skxx.vrxmty.modules.*.*.controller.*.*(..))")
public void before(){}

Match all methods under all controllers under modules, excluding methods in controlelr that call other methods

 

Look here first ---------------------- text----------------------- ----------------------------

The following is reproduced from: https://blog.csdn.net/lmb55/article/details/82470388

1. Sample application scenario: Make a cross-section of all web requests to record logs.

1. Introduce SpringBoot's web module and AOP-related dependencies in pom: 

 <!--10.springboot aop-->
        <!-- start of aop-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- end of aop-->

Among them: 
cglib package is used for dynamic proxy, class-based proxy; 
aspectjrt and aspectjweaver are packages related to aspectj, used to support aspect programming; 
aspectjrt package is the runtime package
of aspectj aspectjweaver is the weaving package of aspectj;

2. Implement a simple web request entry (implement the function of passing in the name parameter and returning "hello xxx"): 
Write picture description here

Note: After the introduction of the AOP dependency package is completed, generally speaking, no other configuration is required. Those who have used the Spring annotation configuration method will ask whether it is necessary to add @EnableAspectJAutoProxy to the main class of the program to enable it, but it is not actually needed.

Because in the default configuration properties of AOP, the spring.aop.auto property is enabled by default, which means that as long as the AOP dependency is introduced, @EnableAspectJAutoProxy has been added by default.

3. Define the aspect class to realize the log aspect of the web layer

To turn a class into an aspect class, two steps are required. 
① Use the @Component annotation on the class to add the aspect class to the IOC container 
② Use the @Aspect annotation on the class to make it an aspect class

package com.example.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * Created by lmb on 2018/9/5.
 */
@Aspect
@Component
public class WebLogAcpect {

    private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class);

    /**
     * 定义切入点,切入点为com.example.aop下的所有函数
     */
    @Pointcut("execution(public * com.example.aop..*.*(..))")
    public void webLog(){}

    /**
     * 前置通知:在连接点之前执行的通知
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(returning = "ret",pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("RESPONSE : " + ret);
    }
}

The above aspect class uses the entry point defined by @Pointcut to cut people for all functions under the com.example.aop package, realizes the advance notification of the entry point through @Before, and records the object returned by the request through @AfterReturning.

Visit http://localhost:8004/hello?name=lmb to get the console output as follows: 
Write picture description here

For detailed code, please refer to my Github: SpringBoot integrates AOP

2. Notifications supported by AOP 
1. Pre-notification @Before: A notification executed before a certain connection point. Unless an exception is thrown, this notification cannot prevent the execution process before the connection point.

/** 
 * 前置通知,方法调用前被调用 
 * @param joinPoint/null
 */  
@Before(value = POINT_CUT)
public void before(JoinPoint joinPoint){
    logger.info("前置通知");
    //获取目标方法的参数信息  
    Object[] obj = joinPoint.getArgs();  
    //AOP代理类的信息  
    joinPoint.getThis();  
    //代理的目标对象  
    joinPoint.getTarget();  
    //用的最多 通知的签名  
    Signature signature = joinPoint.getSignature();  
    //代理的是哪一个方法  
    logger.info("代理的是哪一个方法"+signature.getName());  
    //AOP代理类的名字  
    logger.info("AOP代理类的名字"+signature.getDeclaringTypeName());  
    //AOP代理类的类(class)信息  
    signature.getDeclaringType();  
    //获取RequestAttributes  
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();  
    //从获取RequestAttributes中获取HttpServletRequest的信息  
    HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);  
    //如果要获取Session信息的话,可以这样写:  
    //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);  
    //获取请求参数
    Enumeration<String> enumeration = request.getParameterNames();  
    Map<String,String> parameterMap = Maps.newHashMap();  
    while (enumeration.hasMoreElements()){  
        String parameter = enumeration.nextElement();  
        parameterMap.put(parameter,request.getParameter(parameter));  
    }  
    String str = JSON.toJSONString(parameterMap);  
    if(obj.length > 0) {  
        logger.info("请求的参数信息为:"+str);
    }  
}

Note: JoinPoint and RequestContextHolder are used here. 
1) The signature information of the notification can be obtained through JoinPoint, such as the target method name, target method parameter information, etc.; 
2) The request information and Session information can be obtained through RequestContextHolder;

2. Post notification @AfterReturning: The notification executed after a connection point is usually executed when a matching method returns (the return value can be bound in the post notification).

/** 
 * 后置返回通知 
 * 这里需要注意的是: 
 *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 
 *      如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 
 *       returning:限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,
 *       对于returning对应的通知方法参数为Object类型将匹配任何目标返回值 
 * @param joinPoint 
 * @param keys 
 */  
@AfterReturning(value = POINT_CUT,returning = "keys")  
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){  
    logger.info("第一个后置返回通知的返回值:"+keys);  
}  

@AfterReturning(value = POINT_CUT,returning = "keys",argNames = "keys")  
public void doAfterReturningAdvice2(String keys){  
    logger.info("第二个后置返回通知的返回值:"+keys);  
}

3. Post exception notification @AfterThrowing: notification executed when the method exits with an exception thrown.

/** 
 * 后置异常通知 
 *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; 
 *  throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, 
 *           对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 
 * @param joinPoint 
 * @param exception 
 */  
@AfterThrowing(value = POINT_CUT,throwing = "exception")  
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){  
    //目标方法名:  
    logger.info(joinPoint.getSignature().getName());  
    if(exception instanceof NullPointerException){  
        logger.info("发生了空指针异常!!!!!");  
    }  
}  

4. Post-final notification @After: The notification executed when a connection point exits (regardless of normal return or abnormal exit).

/** 
 * 后置最终通知(目标方法只要执行完了就会执行后置通知方法) 
 * @param joinPoint 
 */  
@After(value = POINT_CUT)  
public void doAfterAdvice(JoinPoint joinPoint){ 
    logger.info("后置最终通知执行了!!!!");  
}  

5. Surround notification @Around: A notification surrounding a connection point, such as method calls. This is the most powerful type of notification. The surrounding notification can complete the custom behavior before and after the method call. It will also choose whether to continue executing the connection point or directly return its own return value or throw an exception to end the execution.

The surround notification is the most powerful and the most troublesome. It is a method of surround. The specific method will be passed to the aspect through the agent. In the aspect, you can choose whether to execute the method or not, and how many methods are executed. The surround notification uses an object of type ProceedingJoinPoint to manage the target object, so the first parameter of this notification must be of type ProceedingJoinPoint. Calling the proceed() method of ProceedingJoinPoint in the notification body will cause the background connection point method to execute. The proceed() method may also be called and an Object[] object is passed in. The value in the array will be used as the input parameter when the method is executed.

/** 
 * 环绕通知: 
 *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 
 *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 
 */  
@Around(value = POINT_CUT)  
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){  
    logger.info("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());  
    try {  
        Object obj = proceedingJoinPoint.proceed();  
        return obj;  
    } catch (Throwable throwable) {  
        throwable.printStackTrace();  
    }  
    return null;  
}  

6. Sometimes when we define an aspect, a certain parameter of the target object needs to be used in the aspect. How can the aspect obtain the target object's parameters? You can use args to bind. If a parameter name is used where a type name should be used in an args expression, then the parameter value of the object will be passed in when the notification is executed.

@Before("execution(* findById*(..)) &&" + "args(id,..)")
    public void twiceAsOld1(Long id){
        System.err.println ("切面before执行了。。。。id==" + id);

    }

Note: Any notification method can define the first parameter as org.aspectj.lang.JoinPoint type (surround notification needs to define the first parameter as ProceedingJoinPoint type, which is a subclass of JoinPoint). The JoinPoint interface provides a series of useful methods, such as getArgs() (return method parameters), getThis() (return proxy object), getTarget() (return target), getSignature() (return information about the method being notified) And toString() (print out useful information about the method being notified).

Third, pointcut expressions 
defined entry point when the need to include a signature name and any parameters, as well as an entry point expression, such as Execution (public * com.example.aop .. . (..))

The format of the entry point expression: execution ([visibility] return type [declaration type]. method name (parameter) [exception]) 
where [] is optional, others also support the use of wildcards: 
1) * : Match all characters 
2) ..: Generally used to match multiple packages, multiple parameters 
3) +: Represents the class and its subclasses 
4) Operators are: &&,||,!

Use cases of entry point expression keywords: 
1) execution: used to match sub-expression. 
// com.cjm.model matching packet and its subpackages all methods in all classes, return type, any, any method parameter 
@Pointcut ( "Execution (com.cjm.model .. * . (..))") 
public void before(){}

2) within: used to match the Java class or package where the connection point is located. 
//Match all methods in the Person class 
@Pointcut(“within(com.cjm.model.Person)”) 
public void before(){} 
//Match all methods in all classes in the com.cjm package and its subpackages 
@Pointcut(“within(com.cjm..*)”) 
public void before(){}

3) this: Used to pass in the proxy object reference to the notification method. 
@Before("before() && this(proxy)") 
public void beforeAdvide(JoinPoint point, Object proxy){ 
//processing logic 
}

4) target: used to pass in the target object reference to the notification method. 
@Before("before() && target(target) 
public void beforeAdvide(JoinPoint point, Object proxy){ 
//processing logic 
}

5) args: used to pass parameters to the notification method. 
@Before("before() && args(age,username)") 
public void beforeAdvide(JoinPoint point, int age, String username){ 
//processing logic 
}

6) @within: It is used to match the class that uses the annotation determined by the parameter at the class level, and all its methods will be matched. 
@Pointcut(“@within(com.cjm.annotation.AdviceAnnotation)”) 
-all classes annotated by @AdviceAnnotation will match 
public void before(){}

7) @target: The function is similar to @within, but the retention policy of the annotation interface must be specified as RUNTIME. 
@Pointcut(“@target(com.cjm.annotation.AdviceAnnotation)”) 
public void before(){}

8) @args: The Java class corresponding to the object passed in the connection point must be marked by the Annotation specified by @args. 
@Before(“@args(com.cjm.annotation.AdviceAnnotation)”) 
public void beforeAdvide(JoinPoint point){ 
//processing logic 
}

9) @annotation: The method of matching the Annotation annotation specified by its parameters. In other words, all methods marked by the specified annotation will match. 
@Pointcut(“@annotation(com.cjm.annotation.AdviceAnnotation)”) 
public void before(){}

10) Bean: Qualify the Bean where the connection point is located by the name of the managed Bean. This keyword is newly added in Spring 2.5. 
@Pointcut(“bean(person)”) 
public void before(){}

Guess you like

Origin blog.csdn.net/ClearLoveQ/article/details/105009900