Maison  >  Article  >  Java  >  Comment implémenter rapidement la fonction Aop dans le projet Springboot

Comment implémenter rapidement la fonction Aop dans le projet Springboot

WBOY
WBOYavant
2023-05-16 23:04:151445parcourir

Introduction aux dépendances

Après que Springboot ait introduit le package de dépendances AOP, d'une manière générale, aucune autre configuration n'est requise dans les versions inférieures ou d'autres configurations affectent les fonctions associées d'AOP, empêchant la fonction aop de prendre effet, vous pouvez essayer d'ajouter @EnableAspectJAutoProxy. à la classe de démarrage pour l'activer ;

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

Implémentation du code

1. Annotation personnalisée @TestAop

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAop {
}

2 ExempleAop.java

@Component
@Aspect
@Slf4j
public class ExampleAop {
 
    //切入点:增强标有@TestAop注解的方法
    @Pointcut(value = "@annotation(TestAop)")
    //切入点签名
    public void pointcut() {
        System.out.println("pointCut签名。。。");
    }
 
    //前置通知
    @Before("pointcut()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        log.info("前置通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    //返回通知
    @AfterReturning(returning = "ret", pointcut = "pointcut()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("返回通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    //异常通知
    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void throwss(JoinPoint jp, Exception ex) {
        log.info("异常通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
        //可以从ex中获取到具体的异常信息
    }
 
    //后置通知
    @After("pointcut()")
    public void after(JoinPoint jp) {
        log.info("后置通知被执行");
        //可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
    }
 
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------环绕通知 start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------环绕通知 end");
        return proceed;
    }
 
}

Annotations et classes principales

1. La classe, comprise simplement, est une encapsulation abstraite de points d'entrée et de notifications, exprimant la correspondance entre les points d'entrée et les méthodes de notification.

La méthode marquée de l'annotation @Before sera exécutée avant que la méthode ne soit mise en exécution, ce qu'on appelle un ; pré-notification

3. @After, post notification, utilisée sur les méthodes, la méthode marquée par l'annotation @After sera exécutée après l'exécution de la méthode cut ;

4, @AfterReturning, notification de retour, utilisée sur les méthodes, annotée ; par @AfterReturning La méthode marquée sera exécutée après que la méthode cut-in ait renvoyé le résultat ;

6 @AfterThrowing : notification d'exception, utilisée pour les méthodes La méthode marquée par l'annotation @AfterThrowing sera exécutée après la méthode cut-in. lève une exception.Il est généralement utilisé pour Le but est d'obtenir des informations sur l'exception ;

7 @Aroud : notification surround, utilisée pour les méthodes La méthode marquée par l'annotation @Around sera exécutée avant et après l'exécution de la méthode cut. ;

8. @Pointcut, point d'entrée, marqué dans la méthode On, il est utilisé pour définir le point d'entrée fait référence à la définition des points de connexion à améliorer, en particulier. fait référence à la définition des méthodes à améliorer ; l'expression annotée avec @Pointcut représente le point d'entrée, les deux plus couramment utilisées sont les expressions d'exécution et les annotations

9. fait référence au point de localisation qui est coupé par l'aspect aop. Au Spring, il fait spécifiquement référence à la méthode qui est coupée en

11, Conseil, notification. la définition des opérations spécifiques qui doivent être effectuées après l'interception du point d'entrée défini ; au Spring, il fait référence aux méthodes marquées d'annotation @Before, @After, @AfterReturning, @AfterThrowing, @Around ; 1. Expression d'exécution

Méthode d'expression : valeur de retour du modificateur d'accès nom du package. nom du package... nom de la classe .Méthode (liste de paramètres)

Exemple 1 : indique qu'il correspond à toutes les méthodes commençant par add dans tous les packages com.fanfu et toutes les classes sous les sous-packages. La valeur de retour et les paramètres ne sont pas limités ;

@Pointcut("execution(* com.fanfu..*.*.add*(..))")

Exemple 2 : indique qu'il correspond à tous les com. Toutes les classes du package fanfu et les sous-packages sont des méthodes qui commencent par add, le type de paramètre est. String, et la valeur de retour n'est pas limitée ;

@Pointcut("execution(* com.fanfu..*.*.add*(String))")

Exemple 3 : Indique qu'elle correspond à n'importe quelle classe, n'importe quelle méthode et n'importe quel paramètre sous le package com.fanfu

@Pointcut("execution(* com.fanfu..*.*.*(String))")

execution() est le corps de l'expression ; premier * indique que le type de valeur de retour est arbitraire, c'est-à-dire que le type de valeur de retour n'est pas limité ;

Le * après le package indique le package actuel, et les deux .. consécutifs après le package indiquent le package et le sous-package actuels ; (..) représente n'importe quel paramètre ;

Le dernier *.*(..) représente la correspondance avec n'importe quelle classe, n'importe quelle méthode, n'importe quel paramètre

2 Annotation


Syntaxe d'annotation : @annotation (Annotation personnalisée)

Exemple : Représente une méthode qui correspond à toutes les annotations de balise @TestAop ;

@Pointcut("@annotation(com.fanfu.config.TestAop)")

Conseils Spring Aop

Chaque @Pointcut peut utiliser l'exécution ou des annotations pour définir des points d'entrée, et la logique peut également être utilisée entre plusieurs opérateurs de points de coupe, tels que &&, || , ! opération ;

1, point1()&&point2() représentent l'intersection de tous les points qui atteignent le point1 et le point2 comme dans l'exemple : le package com.fanfu et toutes les classes de tous les sous-packages, toutes les méthodes dont les noms de méthode ; commence par add et dont le type de paramètre est String intersecte avec toutes les méthodes du package com.fanfu.service et toutes les classes de tous les sous-packages, quel que soit le nom de la méthode et le type de paramètre, c'est-à-dire com.fanfu dans toutes les classes du. package de services et tous les sous-packages, la méthode ou la méthode add1 ou add2 exécutera la logique dans la méthode around() avant et après l'appel

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu..*.*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.service..*.*(..))")
    public void point2() {
    }
    @Pointcut("point1()&&point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

2, point1()&&point2() signifie atteindre le point1 et l'union de tous ; points d'entrée du point2 ; par exemple : dans toutes les classes du package com.fanfu.service et tous les sous-packages subordonnés, le nom de la méthode est add1 et le type de paramètre est String, et toutes les méthodes du package com.fanfu.controller et Dans toutes les classes de tous les sous-packages subordonnés, le nom de la méthode est add2 et le type de paramètre est String. Prenez l'union de toutes les méthodes, c'est-à-dire dans toutes les classes du package com.fanfu.service ou com.fanfu.controller et. tous les sous-packages subordonnés, la méthode Ou la méthode de add1 ou add2, la logique dans la méthode around() sera exécutée avant et après l'appel

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service..*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.controller..*.*.add*(String))")
    public void point2() {
    }
    @Pointcut("point1()||point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

3 !point() signifie l'inversion de tous les points d'entrée qui. hit point(), comme dans l'exemple : com.fanfu Dans toutes les classes du package .service et tous ses sous-packages, les méthodes qui ne commencent pas par add exécuteront la logique de la méthode around() avant et après l'appel .Méthode de correspondance, si dans la chaîne d'appels actuelle, la méthode est mise en correspondance pour la première fois dans la classe actuelle, elle sera touchée, c'est-à-dire que la notification correspondante sera exécutée si la chaîne d'appels actuelle n'est pas terminée, et la. la classe actuelle est appelée dans la méthode actuelle, elle correspondra à d'autres méthodes de point d'entrée, elle ne frappera plus, c'est-à-dire que d'autres notifications associées ne seront plus déclenchées lorsque d'autres méthodes correspondant au point d'entrée seront exécutées

L'exemple suivant ; :

当请求http://localhost:8080/example时,ExampleController中的example方法被触发,ExampleController#example()又调用了ExampleService#test(),在ExampleService#test()内部,又顺序调用了ExampleService#test1()和ExampleService#test2();在ExampleAop中,按照execution中的配置,是可以匹配到test()、test1()、test2(),实际是命中的方法只有test();

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service.impl.ExampleServiceImpl.test*(..))")
    public void point2() {
        log.info("切入点匹配");
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName=null;
        StringBuilder sb=new StringBuilder();
        if (args!=null&&args.length>0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length()>0) {
                argsName=sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};",className,methodName,argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    public String test(String msg) {
        log.info("test方法被执行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
 
    public String test1(String msg) {
        log.info("test1方法被执行");
        return "msg1";
    }
 
    public String test2(String msg) {
        log.info("test2方法被执行");
        return "msg2";
    }
}
public interface IExampleService {
    public String test(String msg);
    public String test1(String msg);
    public String test2(String msg);
}
@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

Comment implémenter rapidement la fonction Aop dans le projet Springboot

2、对于上面的问题,如果把execution表达换成注解,会不会结果不一样?再把ExampleAop中的@Pointcut改成注解形式,再在ExampleService#test1()、ExampleService#test2()、ExampleService#test()添加注解@TestAop,验证结果依然是一样的,只有test()会命中,其他不会!所以要注意呀。

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("@annotation(TestAop)")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中类:{},方法{},参数{};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    @TestAop
    public String test(String msg) {
        log.info("test方法被执行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
    @Override
    @TestAop
    public String test1(String msg) {
        log.info("test1方法被执行");
        return "msg1";
    }
    @Override
    @TestAop
    public String test2(String msg) {
        log.info("test2方法被执行");
        return "msg2";
    }
   
}

3、那什么情况下,ExampleService#test1()、ExampleService#test2()、ExampleService#test()会同时命中呢?让从ExampleController#example()到ExampleService#test1()、ExampleService#test2()、ExampleService#test()分别在不同的调用链上,那么就可以同时命中了;

@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        exampleService.test1(null);
        exampleService.test2(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

Comment implémenter rapidement la fonction Aop dans le projet Springboot

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer