SpringBoot☞ Aspecto AOP para lograr la verificación de permisos: demostración de ejemplo y explicación completa de las anotaciones

Tabla de contenido

1 Entender AOP

1.1 Qué es AOP

1.2 Sistema y concepto de AOP

2 ejemplo de AOP

2.1 El primer ejemplo

2.2 El segundo ejemplo

3 soluciones relacionadas con AOP

3.1 @Pointcut

3.2 @Alrededor

3.3 @Antes

3.4 @Después

3.5 @Después de la vuelta

3.6 @Después del lanzamiento


1 Entender AOP

1.1 Qué es AOP

AOP (Programación Orientada a Aspectos), pensamiento orientado a aspectos, es una de las tres ideas centrales de Spring (dos fuera de las dos: IOC-inversión de control, DI-dependencia de inyección).

Entonces, ¿por qué es tan importante AOP? En nuestros programas, a menudo existen algunos requisitos sistémicos, como verificación de permisos, registro, estadísticas, etc. Estos códigos estarán dispersos e intercalados en diversas lógicas de negocio, que son muy redundantes y no propicias para el mantenimiento. Por ejemplo, el siguiente diagrama:
imagen
cuántas operaciones comerciales hay, cuántos códigos de registro de suma de verificación repetidos deben escribirse, lo cual es obviamente inaceptable. Por supuesto, utilizando el pensamiento orientado a objetos, podemos extraer estos códigos repetitivos y escribirlos como métodos públicos, de la siguiente manera:

imagen
De esta forma, se ha resuelto el problema de la redundancia y mantenibilidad del código, pero en cada método de negocio, sigue siendo necesario llamar manualmente a estos métodos públicos a su vez, lo que también es un poco engorroso. ¿Existe una forma mejor? Sí, eso es AOP. AOP extrae completamente códigos no comerciales, como verificación de permisos y registros de registro, los separa de los códigos comerciales y encuentra nodos para cortar en códigos comerciales:

imagen

 

1.2 Sistema y concepto de AOP

Simplemente entienda, AOP en realidad tiene que hacer tres tipos de cosas:

  • Dónde intervenir, es decir, en qué código comercial se ejecutan las operaciones no comerciales, como la verificación de permisos.

  • Cuándo cortar, antes o después de la ejecución del código comercial.

  • Qué hacer después de iniciar sesión, como verificación de permisos, inicio de sesión, etc.

Por lo tanto, el sistema AOP se puede clasificar como la siguiente figura:
imagen
Algunos conceptos se explican en detalle:

  • Pointcut: Punto de corte, decida dónde el procesamiento, como la verificación de permisos, los registros de registro, etc., se corta en el código comercial (es decir, entretejido en el aspecto) El punto de corte se divide en executionmodo y annotationmodo. El primero puede usar expresiones de ruta para especificar qué clases se entrelazan en el aspecto, y el segundo puede especificar qué anotaciones modifican el código que se entrelazará en el aspecto.

  • Advice: Procesamiento, incluido el tiempo de procesamiento y el contenido de procesamiento. Procesar contenido es lo que se debe hacer, como verificar permisos y registrar registros. El tiempo de procesamiento es cuando se ejecuta el contenido de procesamiento, que se divide en preprocesamiento (es decir, antes de la ejecución del código comercial), posprocesamiento (después de la ejecución del código comercial), etc.

  • Aspect: Sección, a saber Pointcuty Advice.

  • Joint point: El punto de conexión es un punto de ejecución del programa. Por ejemplo, la ejecución de un método o el manejo de una excepción. En Spring AOP, un punto de conexión siempre representa la ejecución de un método.

  • Weaving: Weaving es el proceso de ejecutar el procesamiento de contenido en métodos de objetos de destino a través de agentes dinámicos.

Hay una imagen en Internet, creo que es muy vívida, publícala aquí para que todos la vean:
imagen

2 ejemplo de AOP

El conocimiento real se obtiene de la práctica y luego usaremos el código para implementar AOP. El proyecto completo se ha cargado en:

https://github.com/ThinkMugz/aopDemo

Para utilizar AOP, primero debe introducir las dependencias de AOP. Verificación de parámetros: escriba una verificación de parámetros (validador) como este y no se le disuadirá ~

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.1 El primer ejemplo

A continuación, veamos un ejemplo minimalista: getantes de que se invoquen todas las solicitudes, se emite en la consola una oración "aviso de activación de la solicitud".

La implementación específica es la siguiente:

  1. Para crear una clase de aspecto AOP, simplemente agregue una @Aspect anotación a la clase  . @Aspect La anotación se utiliza para describir una clase de aspecto. Esta anotación es necesaria al definir una clase de aspecto. @Component Las anotaciones se entregan a Spring para que las administre. Implementar consejos en esta clase:

package com.root.demo.advice;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAdvice {
    // 定义一个切点:所有被GetMapping注解修饰的方法会织入advice
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    private void logAdvicePointcut() {}

 // Before表示logAdvice将在目标方法执行前执行
    @Before("logAdvicePointcut()")
    public void logAdvice(){
     // 这里只是一个示例,你可以写任何处理逻辑
        System.out.println("get请求的advice触发了");
    }
}
  1. Cree una clase de interfaz y cree una solicitud de obtención internamente:

package com.root.demo.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/aop")
public class AopController {
    @GetMapping(value = "/getTest")
    public JSONObject aopTest() {
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
    
 @PostMapping(value = "/postTest")
    public JSONObject aopTest2(@RequestParam("id") String id) {
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
}

Después de que comience el proyecto, solicite la http://localhost:8085/aop/getTestinterfaz:

imagen

Inserte la descripción de la imagen aquí

Se solicita la http://localhost:8085/aop/postTestinterfaz y no hay salida de la consola, lo que demuestra que el punto de corte es solo para el GetMappingmétodo modificado.

2.2 El segundo ejemplo

Compliquemos un poco el problema. El escenario de este ejemplo es:

  1. Personalizar una anotaciónPermissionsAnnotation

  2. Cree una clase de aspecto, establezca el punto de corte para interceptar todos los PermissionsAnnotationmétodos marcados , interceptar los parámetros de la interfaz y realizar una verificación de permisos simple

  3. La PermissionsAnnotationclase de interfaz de prueba de interfaz de prueba marcada testen

Pasos de implementación específicos:

  1. Utilice @Target、@Retention、@Documenteduna anotación personalizada:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionAnnotation{
}
  1. Para crear la primera clase de aspecto AOP, simplemente agregue una @Aspect anotación a la clase  . @Aspect La anotación se utiliza para describir una clase de aspecto. Esta anotación es necesaria al definir una clase de aspecto. @Component Las anotaciones se entregan a Spring para que las administre. Implemente la lógica de verificación de permisos del primer paso en esta clase:

package com.root.example.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(1)
public class PermissionFirstAdvice {

 // 定义一个切面,括号内写入第1步中自定义注解的路径
    @Pointcut("@annotation(com.mu.demo.annotation.PermissionAnnotation)")
    private void permissionCheck() {
    }

    @Around("permissionCheck()")
    public Object permissionCheckFirst(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("===================第一个切面===================:" + System.currentTimeMillis());

        //获取请求参数,详见接口类
        Object[] objects = joinPoint.getArgs();
        Long id = ((JSONObject) objects[0]).getLong("id");
        String name = ((JSONObject) objects[0]).getString("name");
        System.out.println("id1->>>>>>>>>>>>>>>>>>>>>>" + id);
        System.out.println("name1->>>>>>>>>>>>>>>>>>>>>>" + name);

        // id小于0则抛出非法id的异常
        if (id < 0) {
            return JSON.parseObject("{\"message\":\"illegal id\",\"code\":403}");
        }
        return joinPoint.proceed();
    }
}
  1. Cree una clase de interfaz y marque una anotación personalizada en el método de destino  PermissionsAnnotation:

package com.root.example.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/permission")
public class TestController {
    @RequestMapping(value = "/check", method = RequestMethod.POST)
    // 添加这个注解
    @PermissionsAnnotation()
    public JSONObject getGroupList(@RequestBody JSONObject request) {
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
    }
}

Aquí, primero realizamos una prueba. Primero, complete la dirección y el encabezado de la solicitud:

imagen

 

En segundo lugar, construya parámetros normales:

imagen

 

Puede obtener el resultado de respuesta normal:

imagen

Luego, construya un parámetro anormal y solicite nuevamente:

imagen

 

El resultado de la respuesta muestra que la clase de aspecto ha sido juzgada y se devuelve el resultado correspondiente:

imagen

Algunas personas pueden preguntar, ¿qué sucede si quiero establecer varias clases de aspecto para la verificación en una interfaz? ¿Cómo gestionar el orden de ejecución de estos aspectos?

Es muy simple. Una AOPanotación personalizada puede corresponder a múltiples clases de aspecto. El orden de ejecución de estas clases de aspecto es administrado por la @Orderanotación. Cuanto menor sea el número después de la anotación, más se ejecutará primero la clase de aspecto.

Lo siguiente se demuestra en un ejemplo:

Cree una segunda clase de aspecto AOP e implemente el segundo paso de verificación de permisos en esta clase:

package com.root.example.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(0)
public class PermissionSecondAdvice {

   @Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
   private void permissionCheck() {
   }

   @Around("permissionCheck()")
   public Object permissionCheckSecond(ProceedingJoinPoint joinPoint) throws Throwable {
       System.out.println("===================第二个切面===================:" + System.currentTimeMillis());

       //获取请求参数,详见接口类
       Object[] objects = joinPoint.getArgs();
       Long id = ((JSONObject) objects[0]).getLong("id");
       String name = ((JSONObject) objects[0]).getString("name");
       System.out.println("id->>>>>>>>>>>>>>>>>>>>>>" + id);
       System.out.println("name->>>>>>>>>>>>>>>>>>>>>>" + name);

       // name不是管理员则抛出异常
       if (!name.equals("admin")) {
           return JSON.parseObject("{\"message\":\"not admin\",\"code\":403}");
       }
       return joinPoint.proceed();
   }
}

Reinicie el proyecto, continúe con las pruebas y cree una situación en la que ambos parámetros sean anormales:

imagen

En respuesta a los resultados, el orden de ejecución de la segunda clase de aspecto parece ser mayor:

imagen

 

3 soluciones relacionadas con AOP

En el caso anterior, se utilizan muchas anotaciones. La siguiente es una explicación detallada de estas anotaciones.

3.1 @Pointcut

@Pointcut La anotación se utiliza para definir un aspecto, es decir, el punto de entrada de algo relacionado anteriormente, y el punto de entrada define el momento en que se desencadena el evento.

@Aspect
@Component
public class LogAspectHandler {

    /**
     * 定义一个切面,拦截 com.itcodai.course09.controller 包和子包下的所有方法
     */
    @Pointcut("execution(* com.mutest.controller..*.*(..))")
    public void pointCut() {}
}

La anotación @Pointcut especifica un aspecto y define lo que debe ser interceptado. Aquí hay dos expresiones de uso común: una es usar  execution()y la otra es usar  annotation().

Más referencia: agregación de contenido SpringBoot

expresión de ejecución:

Tome la  execution(* * com.mutest.controller..*.*(..))) expresión como ejemplo:

  • La posición del primer *: indica el tipo de valor de retorno, * indica todos los tipos.

  • Nombre del paquete: indica el nombre del paquete que debe ser interceptado. Los dos puntos detrás indican el paquete actual y todos los subpaquetes del paquete actual. En este ejemplo, se refiere al paquete com.mutest.controller y los métodos de todas las clases en el subpaquete.

  • La posición del segundo * signo: indica el nombre de la clase, * indica todas las clases.

  • * (..): Este asterisco indica el nombre del método, * indica todos los métodos, y los paréntesis indican los parámetros del método, y dos puntos indican cualquier parámetro.

expresión annotation ():

annotation() La forma es definir el aspecto para una determinada anotación. Por ejemplo, si hacemos el aspecto para el método con la anotación @PostMapping, podemos definir el aspecto de la siguiente manera:

@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void annotationPointcut() {}

Luego, si usa este aspecto, @PostMapping todos los métodos de la anotación se cortarán  . Este método es muy adecuado para @GetMapping、@PostMapping、@DeleteMappingescenarios de procesamiento  en los que diferentes anotaciones tienen varias lógicas de procesamiento específicas.

También existe la definición de aspectos para anotaciones personalizadas como se muestra en el caso anterior.

@Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
private void permissionCheck() {}

3.2 @Alrededor

@AroundLas anotaciones se utilizan para modificar y Aroundmejorar el procesamiento. El Aroundprocesamiento mejorado es muy poderoso y se manifiesta en:

  1. @AroundEl orden de ejecución de la acción de mejora y el método objetivo se pueden seleccionar libremente, lo que significa que el método objetivo puede ejecutarse antes y después de la acción de mejora, o incluso durante el proceso. La realización de esta característica es que ProceedingJoinPointel procedd()método que llama al parámetro ejecutará el método de destino.

  2. @AroundPuede cambiar los valores de los parámetros del método de destino de ejecución y también puede cambiar el valor de retorno después de la ejecución del método de destino.

AroundEl procesamiento mejorado tiene las siguientes características:

  1. Al definir un Aroundmétodo de procesamiento mejorado, el primer parámetro del método debe ser un  ProceedingJoinPoint tipo (al menos un parámetro). Procesamiento de mejora in vivo, llamar ProceedingJoinPointal proceedmétodo ejecutará el método de destino: este es el @Aroundmétodo de procesamiento mejorado que puede controlar completamente el tiempo de ejecución del objetivo, cómo realizar la clave; si el programa no llama ProceedingJoinPointal proceedmétodo, el método de destino lo hace.

  2. Al llamar a ProceedingJoinPointun proceedmétodo, también puede pasar un Object[ ]objeto, y el valor de la matriz se pasará al método de destino como un parámetro real; esta es la clave para el Aroundmétodo de procesamiento mejorado que puede cambiar el valor del parámetro del método de destino . Esto es así si la Object[ ]longitud de la matriz pasada no es igual al número de parámetros requeridos por el método de destino, o el Object[ ]elemento de la matriz no coincide con el tipo de parámetros requeridos por el método de destino, el programa será anormal.

@AroundAunque es poderoso, generalmente debe usarse en un entorno seguro para subprocesos. Por lo tanto, si usa común Before, AfterReturningpodrá resolver el problema, no es necesario que lo Aroundagote. Si necesita compartir algunos datos de estado antes y después de que se ejecute el método de destino, debería considerar su uso Around. Especialmente cuando necesita utilizar un procesamiento mejorado para evitar la ejecución del objetivo, o necesita cambiar el valor de retorno del método objetivo, solo puede utilizar el Aroundprocesamiento mejorado.

A continuación, realice algunas modificaciones sobre el ejemplo anterior para observar @Aroundlas características.

La clase de anotación personalizada permanece sin cambios. Primero, defina la clase de interfaz:

package com.example.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/permission")
public class TestController {
    @RequestMapping(value = "/check", method = RequestMethod.POST)
    @PermissionsAnnotation()
    public JSONObject getGroupList(@RequestBody JSONObject request) {
        return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200,\"data\":" + request + "}");
    }
}

El único tipo de faceta (hay dos tipos de faceta en el caso anterior, solo mantenga una aquí):

package com.root.example.demo;

import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;


@Aspect
@Component
@Order(1)
public class PermissionAdvice {

    @Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
    private void permissionCheck() {
    }


    @Around("permissionCheck()")
    public Object permissionCheck(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("===================开始增强处理===================");

        //获取请求参数,详见接口类
        Object[] objects = joinPoint.getArgs();
        Long id = ((JSONObject) objects[0]).getLong("id");
        String name = ((JSONObject) objects[0]).getString("name");
        System.out.println("id1->>>>>>>>>>>>>>>>>>>>>>" + id);
        System.out.println("name1->>>>>>>>>>>>>>>>>>>>>>" + name);

  // 修改入参
        JSONObject object = new JSONObject();
        object.put("id", 8);
        object.put("name", "lisi");
        objects[0] = object;
  
  // 将修改后的参数传入
        return joinPoint.proceed(objects);
    }
}

También use JMeter para llamar a la interfaz, pase el parámetro: {"id":-5,"name":"admin"}y el resultado de la respuesta muestra que los parámetros de entrada de @Aroundla interfaz están interceptados y la interfaz devuelve el resultado en la clase de aspecto.
imagen

3.3 @Antes

@Before El método especificado por la anotación se ejecuta antes de que el aspecto corte en el método de destino. Puede hacer algún  Log procesamiento o estadísticas de información, como obtener la solicitud  URL del usuario y la IP dirección del usuario  , etc. Esto se puede utilizar al hacer un sitio personal. Es un método de uso común. Por ejemplo, el siguiente código:

@Aspect
@Component
@Slf4j
public class LogAspectHandler {
    /**
     * 在上面定义的切面方法之前执行该方法
     * @param joinPoint jointPoint
     */
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("====doBefore方法进入了====");

        // 获取签名
        Signature signature = joinPoint.getSignature();
        // 获取切入的包名
        String declaringTypeName = signature.getDeclaringTypeName();
        // 获取即将执行的方法名
        String funcName = signature.getName();
        log.info("即将执行方法为: {},属于{}包", funcName, declaringTypeName);

        // 也可以用来记录一些信息,比如获取请求的 URL 和 IP
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取请求 URL
        String url = request.getRequestURL().toString();
        // 获取请求 IP
        String ip = request.getRemoteAddr();
        log.info("用户请求的url为:{},ip地址为:{}", url, ip);
    }
}

JointPoint El objeto es muy útil, puedes usarlo para obtener una firma, y ​​usar la firma para obtener el nombre del paquete solicitado, el nombre del método, incluyendo los parámetros (  joinPoint.getArgs() obteniendo), etc.

3.4 @Después

@After La anotación  @Before corresponde a una anotación. El método especificado se ejecuta después de que el aspecto corta el método de destino, o algún procesamiento de registro después de que se completa un determinado método.

@Aspect
@Component
@Slf4j
public class LogAspectHandler {
    /**
     * 定义一个切面,拦截 com.mutest.controller 包下的所有方法
     */
    @Pointcut("execution(* com.mutest.controller..*.*(..))")
    public void pointCut() {}

    /**
     * 在上面定义的切面方法之后执行该方法
     * @param joinPoint jointPoint
     */
    @After("pointCut()")
    public void doAfter(JoinPoint joinPoint) {

        log.info("==== doAfter 方法进入了====");
        Signature signature = joinPoint.getSignature();
        String method = signature.getName();
        log.info("方法{}已经执行完", method);
    }
}

En este punto, escribamos un controlador para probar los resultados de la ejecución y creemos un nuevo AopController de la siguiente manera:

@RestController
@RequestMapping("/aop")
public class AopController {

    @GetMapping("/{name}")
    public String testAop(@PathVariable String name) {
        return "Hello " + name;
    }
}

Inicie el proyecto, ingrese: localhost: 8080 / aop / csdn en el navegador y observe la información de salida de la consola:

====doBefore 方法进入了====  
即将执行方法为: testAop,属于com.itcodai.mutest.AopController包  
用户请求的 url 为:http://localhost:8080/aop/name,ip地址为:0:0:0:0:0:0:0:1  
==== doAfter 方法进入了====  
方法 testAop 已经执行完

Desde la impresión  Log se puede ver con la secuencia lógica de ejecución del programa, el control puede ser muy intuitivo  @Before y el  @After efecto práctico de dos anotaciones.

3.5 @Después de la vuelta

@AfterReturning Las anotaciones  @After son algo similares, la diferencia es que las  @AfterReturning anotaciones se pueden usar para capturar el valor de retorno después de que se ejecuta el método de corte y realizar el procesamiento de mejora de la lógica empresarial en el valor de retorno, por ejemplo:

@Aspect
@Component
@Slf4j
public class LogAspectHandler {
    /**
     * 在上面定义的切面方法返回后执行该方法,可以捕获返回对象或者对返回对象进行增强
     * @param joinPoint joinPoint
     * @param result result
     */
    @AfterReturning(pointcut = "pointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {

        Signature signature = joinPoint.getSignature();
        String classMethod = signature.getName();
        log.info("方法{}执行完毕,返回参数为:{}", classMethod, result);
        // 实际项目中可以根据业务做具体的返回值增强
        log.info("对返回参数进行业务上的增强:{}", result + "增强版");
    }
}

Cabe señalar que en la  @AfterReturning anotación, returning el valor del atributo  debe ser consistente con el parámetro, de lo contrario no será detectado. El segundo parámetro de entrada en este método es el valor de retorno del método de corte. El valor de retorno doAfterReturning se puede mejorar en el  método y se puede encapsular de acuerdo con las necesidades comerciales. Reiniciemos el servicio y probémoslo nuevamente:

方法 testAop 执行完毕,返回参数为:Hello CSDN  
对返回参数进行业务上的增强:Hello CSDN 增强版

3.6 @Después del lanzamiento

Cuando se lanza una excepción durante la ejecución del método de corte, se  @AfterThrowing ejecutará en el método anotado, y en este método se puede realizar alguna lógica de manejo de excepciones. Cabe señalar que el throwing valor del  atributo debe ser consistente con el parámetro, de lo contrario se reportará un error. El segundo parámetro de este método es la excepción lanzada.

@Aspect
@Component
@Slf4j
public class LogAspectHandler {
    /**
     * 在上面定义的切面方法执行抛异常时,执行该方法
     * @param joinPoint jointPoint
     * @param ex ex
     */
    @AfterThrowing(pointcut = "pointCut()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        Signature signature = joinPoint.getSignature();
        String method = signature.getName();
        // 处理异常的逻辑
        log.info("执行方法{}出错,异常为:{}", method, ex);
    }
}

Supongo que te gusta

Origin blog.csdn.net/baidu_39322753/article/details/111274598
Recomendado
Clasificación