SpringBoot integra aspectos de AOP y comprensión simple de expresiones de puntos de entrada

Después de leer el texto, entendamos esta expresión EZ

Lea el siguiente artículo primero y luego mire esta parte

Primero, déjame hablar sobre la expresión del punto de entrada y luego leerás el siguiente artículo

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

Haga coincidir todos los métodos en todos los controladores en los módulos, excluyendo los métodos en controlelr que llaman a otros métodos

 

Mira aquí primero ---------------------- texto ----------------------- ----------------------------

Lo siguiente se reproduce en: https://blog.csdn.net/lmb55/article/details/82470388

1. Escenario de aplicación de muestra: haga una sección transversal de todas las solicitudes web para registrar registros.

1. Introduzca el módulo web de SpringBoot y las dependencias relacionadas con AOP en pom: 

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

Entre ellos: el 
paquete cglib se utiliza para proxy dinámico, proxy basado en clases; 
Aspectjrt y Aspectjweaver son paquetes relacionados con Aspectj, utilizados para admitir la programación de
Aspectos El paquete Aspectjrt es el paquete en tiempo
de ejecución de Aspectoj Aspectojweaver es el paquete de tejido de Aspectj;

2. Implemente una entrada de solicitud web simple (implemente la función de pasar el parámetro de nombre y devolver "hola xxx"): 
Escriba la descripción de la imagen aquí

Nota: Una vez que se completa la introducción del paquete de dependencia AOP, en términos generales, no se requiere ninguna otra configuración. Aquellos que hayan utilizado el método de configuración de anotaciones Spring preguntarán si es necesario agregar @EnableAspectJAutoProxy a la clase principal del programa para habilitarlo, pero en realidad no es necesario.

Porque en las propiedades de configuración predeterminadas de AOP, la propiedad spring.aop.auto está habilitada de forma predeterminada, lo que significa que mientras se introduzca la dependencia de AOP, @EnableAspectJAutoProxy se ha agregado de forma predeterminada.

3. Defina la clase de aspecto para realizar el aspecto de registro de la capa web.

Para convertir una clase en una clase de aspecto, se requieren dos pasos. 
① Use la anotación @Component en la clase para agregar la clase de aspecto al contenedor IOC 
② Use la anotación @Aspect en la clase para convertirla en una clase de aspecto

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);
    }
}

La clase de aspecto anterior usa el punto de entrada definido por @Pointcut para cortar personas para todas las funciones bajo el paquete com.example.aop, realiza la notificación anticipada del punto de entrada a través de @Before y registra el objeto devuelto por la solicitud a través de @AfterReturning.

Visite http: // localhost: 8004 / hello? Name = lmb para obtener el resultado de la consola de la siguiente manera: 
Escriba la descripción de la imagen aquí

Para obtener un código detallado, consulte mi Github: SpringBoot integra AOP

2. Notificaciones soportadas por AOP 
1. Pre-notificación @Before: Una notificación ejecutada antes de un punto de conexión determinado. A menos que se produzca una excepción, esta notificación no puede evitar el proceso de ejecución antes del punto de conexión.

/** 
 * 前置通知,方法调用前被调用 
 * @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);
    }  
}

Nota: Aquí se utilizan JoinPoint y RequestContextHolder. 
1) La información de la firma de la notificación se puede obtener a través de JoinPoint, como el nombre del método de destino, la información de los parámetros del método de destino, etc .; 
2) La información de la solicitud y la información de la sesión se pueden obtener a través de RequestContextHolder;

2. Publicar notificación @AfterReturning: La notificación ejecutada después de un punto de conexión generalmente se ejecuta cuando regresa un método coincidente (el valor de retorno se puede vincular en la notificación posterior).

/** 
 * 后置返回通知 
 * 这里需要注意的是: 
 *      如果参数中的第一个参数为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. Publicar notificación de excepción @AfterThrowing: notificación ejecutada cuando el método lanza una excepción y sale.

/** 
 * 后置异常通知 
 *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; 
 *  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. Notificación post-final @After: La notificación que se ejecuta cuando un punto de conexión sale (independientemente del retorno normal o salida anormal).

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

5. Notificación envolvente @Around: una notificación que rodea un punto de conexión, como las llamadas a métodos. Este es el tipo de notificación más poderoso. La notificación circundante puede completar el comportamiento personalizado antes y después de la llamada al método. También elegirá si continuar ejecutando el punto de conexión o devolver directamente su propio valor de retorno o lanzar una excepción para finalizar la ejecución.

La notificación envolvente es la más poderosa y la más problemática. Es un método de envolvente. El método específico se pasará al aspecto a través del agente. En el aspecto, puedes elegir si ejecutar el método o no y cuántos métodos se ejecutan. La notificación envolvente utiliza un objeto de tipo ProceedingJoinPoint para administrar el objeto de destino, por lo que el primer parámetro de esta notificación debe ser de tipo ProceedingJoinPoint. Llamar al método continue () de ProceedingJoinPoint en el cuerpo de la notificación hará que se ejecute el método de punto de conexión en segundo plano. También se puede llamar al método continue () y se pasa un objeto Object []. El valor en la matriz se usará como parámetro de entrada cuando se ejecute el método.

/** 
 * 环绕通知: 
 *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 
 *   环绕通知第一个参数必须是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. A veces, cuando definimos un aspecto, es necesario utilizar un determinado parámetro del objeto objetivo en el aspecto ¿Cómo puede el aspecto obtener los parámetros del objeto objetivo? Puede usar args para enlazar. Si se usa un nombre de parámetro donde se debe usar un nombre de tipo en una expresión args, entonces el valor del parámetro del objeto se pasará cuando se ejecute la notificación.

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

    }

Nota: Cualquier método de notificación puede definir el primer parámetro como tipo org.aspectj.lang.JoinPoint (la notificación envolvente debe definir el primer parámetro como tipo ProceedingJoinPoint, que es una subclase de JoinPoint). La interfaz JoinPoint proporciona una serie de métodos útiles, como getArgs () (parámetros de método de retorno), getThis () (objeto de retorno de proxy), getTarget () (destino de retorno), getSignature () (información de retorno sobre el método notificado) Y toString () (imprime información útil sobre el método que se notifica).

En tercer lugar, las expresiones punto de corte 
definidos punto de entrada cuando la necesidad de incluir un nombre de la firma y los parámetros, así como una expresión punto de entrada, como por ejemplo de ejecución (* com.example.aop pública .. . (..))

El formato de la expresión del punto de entrada: ejecución ([visibilidad] tipo de retorno [tipo de declaración]. Nombre del método (parámetro) [excepción]) 
donde [] es opcional, otros también admiten el uso de comodines: 
1) * : Coincide con todos los caracteres 
2) ..: Generalmente se usa para emparejar múltiples paquetes, múltiples parámetros 
3) +: Representa la clase y sus subclases 
4) Los operadores son: &&, || ,!

Casos de uso de palabras clave de expresión de pointcut:
1) ejecución: se utiliza para hacer coincidir la subexpresión 
// paquete coincidente com.cjm.model y sus subpaquetes todos los métodos en todas las clases, tipo de retorno, cualquiera, cualquier parámetro de método 
@Pointcut ("Ejecución (com.cjm.model .. * . (..))") 
vacío público antes () {}

2) inside: se utiliza para coincidir con la clase o paquete Java donde se encuentra el punto de conexión. 
// Coincide con todos los métodos en la clase Person 
@Pointcut (“dentro de (com.cjm.model.Person)”) 
public void before () {} 
// Coincide con todos los métodos en todas las clases en el paquete com.cjm y sus subpaquetes 
@Pointcut ("dentro de (com.cjm .. *)") 
public void before () {}

3) esto: se utiliza para pasar la referencia del objeto proxy al método de notificación. 
@Before ("before () && this (proxy)") 
public void beforeAdvide (punto JoinPoint, proxy de objeto) { 
// lógica de procesamiento 
}

4) objetivo: se utiliza para pasar la referencia del objeto objetivo al método de notificación. 
@Before ("before () && target (target) 
public void beforeAdvide (punto JoinPoint, proxy de objeto) { 
// lógica de procesamiento 
}

5) args: se utiliza para pasar parámetros al método de notificación. 
@Before ("before () && args (age, username)") 
public void beforeAdvide (JoinPoint point, int age, String username) { 
// lógica de procesamiento 
}

6) @within: Se usa para hacer coincidir la clase que usa la anotación determinada por el parámetro a nivel de clase, y todos sus métodos serán comparados. 
@Pointcut (“@ within (com.cjm.annotation.AdviceAnnotation)”) 
-todas las clases anotadas por @AdviceAnnotation coincidirán con 
el vacío público antes de () {}

7) @target: la función es similar a @within, pero la política de retención de la interfaz de anotación debe especificarse como RUNTIME. 
@Pointcut (“@ target (com.cjm.annotation.AdviceAnnotation)”) 
public void before () {}

8) @args: La clase Java correspondiente al objeto pasado en el punto de conexión debe estar marcada por la Anotación especificada por @args. 
@Before (“@ args (com.cjm.annotation.AdviceAnnotation)”) 
public void beforeAdvide (punto JoinPoint) { 
// lógica de procesamiento 
}

9) @annotation: El método para hacer coincidir la anotación de anotación especificada por sus parámetros. En otras palabras, todos los métodos marcados por la anotación especificada coincidirán. 
@Pointcut (“@ annotation (com.cjm.annotation.AdviceAnnotation)”) 
public void before () {}

10) Bean: El Bean donde se encuentra el punto de conexión está limitado por el nombre del Bean administrado. Esta palabra clave se agregó recientemente en Spring 2.5. 
@Pointcut ("bean (persona)") 
public void before () {}

Supongo que te gusta

Origin blog.csdn.net/ClearLoveQ/article/details/105009900
Recomendado
Clasificación