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>
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; } }
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)
@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ètre2 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; } }
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()); } }
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()); } }
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!