Selepas Springboot memperkenalkan pakej kebergantungan AOP, secara amnya, tidak perlu melakukan konfigurasi lain Dalam versi yang lebih rendah atau konfigurasi lain mempengaruhi fungsi AOP yang berkaitan, mengakibatkan ketidakupayaan fungsi aop. Untuk melaksanakannya, anda boleh cuba menambahkan @EnableAspectJAutoProxy ke kelas permulaan untuk mendayakannya; . ExampleAop.java
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
1, menunjukkan bahawa kelas semasa ialah kelas aspek Pemahaman yang mudah ialah pengkapsulan abstrak bagi titik masuk dan pemberitahuan hubungan antara titik masuk dan kaedah pemberitahuan;
Kaedah yang ditandakan dengan @Sebelum anotasi akan dilaksanakan sebelum kaedah dipotong menjadi pelaksanaan, yang dipanggil pra-pemberitahuan
4 @AfterReturning, kembalikan pemberitahuan, digunakan untuk kaedah, kaedah yang ditandakan; @AfterReturning anotasi akan dilaksanakan selepas kaedah potong mengembalikan hasil ;
6 @AfterThrowing: Pemberitahuan pengecualian, digunakan pada kaedah yang ditandakan dengan anotasi @AfterThrowing akan dilaksanakan selepas lontaran kaedah potong. Pengecualian. Ia biasanya digunakan untuk mendapatkan maklumat pengecualian untuk tujuan;
7 @Aroud: pemberitahuan sekeliling, digunakan untuk kaedah yang ditandakan oleh anotasi @Around akan dilaksanakan sebelum dan selepas pelaksanaan kaedah potong masuk;
8 @Pointcut, titik potong, ditanda dalam kaedah pada, digunakan untuk mentakrifkan titik masuk merujuk kepada takrifan titik sambungan Dipertingkatkan. Dalam Spring, ia secara khusus merujuk kepada takrifan kaedah yang hendak dipertingkatkan; 🎜>9. Titik sambungan, yang dipanggil titik sambungan merujuk kepada titik kedudukan yang dipotong oleh aspek aop, khususnya dalam Spring ia merujuk kepada Kaedah untuk dipotong; >11. Nasihat, pemberitahuan merujuk kepada takrifan operasi khusus yang akan dilakukan selepas memintas titik masuk yang ditakrifkan dalam Musim Bunga, ia merujuk kepada kaedah yang ditandakan oleh @Sebelum, @Selepas, @Selepas, @AfterThrowing, dan @Around anotasi;
Cara biasa untuk menandakan titik masuk
1 ungkapan pelaksanaan
Kaedah ungkapan: nama pakej nilai pulangan pengubahsuai...nama kelas... .method (senarai parameter)
Contoh 1: Menunjukkan padanan semua pakej com.fanfu dan sub-pakej Kaedah bermula dengan tambah dalam semua kelas, nilai pulangan dan parameter tidak terhad; Contoh 2: Menunjukkan padanan semua kelas bermula dengan tambah dalam semua pakej com.fanfu dan sub-pakej, dan jenis parameter ialah kaedah String, nilai pulangan tidak terhad; , sebarang kaedah dan sebarang parameter di bawah pakej com.fanfu
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TestAop { }
* pertama menunjukkan bahawa jenis nilai pulangan adalah sewenang-wenangnya; ialah, jenis nilai pulangan tidak terhad;
* selepas pakej menunjukkan pakej semasa, dan dua berturut-turut .. selepas pakej menunjukkan pakej semasa dan sub-pakej; parameter;@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; } }
Contoh berikut:@Pointcut("execution(* com.fanfu..*.*.add*(..))")Salin selepas log masuk2, point1()&&point2() mewakili kesatuan semua titik potong yang memukul titik1 dan titik2; com.fanfu.service package dan Antara semua kelas dalam semua sub-pakej, nama kaedah ialah add1 dan jenis parameter ialah String Dalam pakej com.fanfu.controller dan semua kelas dalam semua sub-pakej, nama kaedah ialah add2 dan jenis parameter ialah String Kaedahnya ialah kesatuan, iaitu, dalam semua kelas pakej com.fanfu.service atau com.fanfu.controller dan semua sub-pakej bawahan, kaedah atau kaedah add1 atau add2 akan laksanakan pemberitahuan sekeliling sebelum dan selepas memanggil kaedah around()
@Pointcut("execution(* com.fanfu..*.*.add*(String))")Salin selepas log masuk
3 !point() bermaksud penyongsangan semua titik masuk yang terkena point(), seperti contoh: dalam semua kelas daripada pakej com.fanfu.service dan semua sub-pakej subordinatnya, bukannya menambah Kaedah pada permulaan akan melaksanakan logik dalam kaedah around() sebelum dan selepas panggilan@Pointcut("execution(* com.fanfu..*.*.*(String))")Salin selepas log masuk
Nota Spring Aop< . dilaksanakan jika rantai panggilan semasa belum berakhir, dan kaedah pemadanan kelas semasa dengan titik masuk lain dipanggil dalam kaedah semasa, ia tidak akan memukul lagi pemberitahuan apabila dilaksanakan;
当请求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()); } }
Atas ialah kandungan terperinci Bagaimana untuk melaksanakan fungsi Aop dengan cepat dalam projek Springboot. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!