当前位置:

首页 > 编程开发 > Springboot项目如何快速实现Aop功能

Springboot项目如何快速实现Aop功能

依赖引入Springboot引入AOP依赖包后,一般来说是不需要再做其他配置了,在比较低的版本或者有其他配置影响了AOP的相关功能,导致aop功能不生效,可以试试在启动类上增加@EnableAspectJAutoProxy来启用;org.springframework.bootspring-boot-starter-aop代码实现1、自定义注解@TestAop@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@inter

本文目录

    依赖引入

    Springboot引入AOP依赖包后,一般来说是不需要再做其他配置了,在比较低的版本或者有其他配置影响了AOP的相关功能,导致aop功能不生效,可以试试在启动类上增加@EnableAspectJAutoProxy来启用;

    
        org.springframework.boot
        spring-boot-starter-aop
    

    代码实现

    1、自定义注解@TestAop

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

    2、ExampleAop .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、Aspect,表示当前类是一个切面类,简单理解就是切入点和通知的抽象封装,表述的是切入点和通知方法之间的对应关系;

    2、@Before,前置通知,用于方法上,被@Before注解标记的方法会在被切入方法执行之前执行;

    3、@After,后置通知,用于方法上,被@After注解标记的方法会在被切入方法执行之后执行;

    4、@AfterReturning,返回通知,用于方法上,被@AfterReturning注解标记的方法会在被切入方法返回结果之后执行;

    6、@AfterThrowing:异常通知,用于方法上,被@AfterThrowing注解标记的方法会在被切入方法抛出异常之后执行,一般用于有目的的获取异常信息;

    7、@Aroud:环绕通知,用于方法上,被@Around注解标记的方法会在被切入方法执行前后执行;

    8、@Pointcut,切入点,标记在方法上,用于定义切入点,所谓的切入点是指对哪些连接点处进行切入增强的定义,在Spring中具体就是指对哪些方法进行切入增强的定义;被@Pointcut注解表示切入点的表达式有多种,最常用的是两种,execution表达式和注解;

    9、Jointpoint,连接点,所谓的连接点是指被aop切面切入的位置点,在Spring中具体就是指被切入的方法;

    10、PointCut,

    11、Advice,通知,所谓的通知是指对定义好的切入点进行拦截后,要具体做哪些操作的定义;在Spring中就是指被@Before、@After、@AfterReturning、@AfterThrowing、@Around注解标记的方法;

    标记切入点的常用方式

    1、execution表达式

    表达式请法:访问修饰符 返回值 包名.包名...类名.方法(参数列表)

    示例1:表示匹配所有com.fanfu包以及子包下的所有类中以add开头的方法,返回值、参数不限;

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

    示例2:表示匹配所有com.fanfu包以及子包下的所有类中以add开头,参数类型是String的方法,返回值不限;

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

    示例3:表示匹配com.fanfu包下任意类、任意方法、任意参数;

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

    execution()为表达式的主体;
    第一个*表示返回值类型为任意,即不限制返回值类型;
    包后的*表示当前包,包后面连续两个..表示当前包以及子包;
    (..)表示任意参数;
    最后的*.*(..)表示匹配任意类、任意方法、任意参数;

    2、注解

    注解语法:@annotation(自定义的注解)

    示例:表示匹配所有标记@TestAop注解的方法;

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

    Spring Aop的小技巧

    每一个@Pointcut可以使用execution或注解来定义切入点,多个切点之间还可以使用逻辑运算符,如&&、||、!运算;

    1、point1()&&point2()表示命中point1和point2的所有切入点的交集;如示例:com.fanfu包以及下属所有子包的所有类中,方法名是以add开头,参数类型是String的所有方法,与com.fanfu.service包以及下属所有子包的所有类中,不限方法名和参数类型的所有方法取交集,即com.fanfu.service包以及下属所有子包的所有类中,方法或是add1或add2的方法,在调用前后都会执行环绕通知around()方法内的逻辑;

    @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()表示命中point1和point2的所有切入点的并集;如示例:com.fanfu.service包以及下属所有子包的所有类中,方法名是add1,参数类型是String的所有方法,与com.fanfu.controller包以及下属所有子包的所有类中,方法名是add2,参数类型是String的所有方法取并集,即com.fanfu.service或com.fanfu.controller的包以及下属所有子包的所有类中,方法或是add1或add2的方法,在调用前后都会执行环绕通知around()方法内的逻辑;

    @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()表示命中point()的所有切入点取反,如示例:com.fanfu.service包及下属所有子包的所有类中,不是以add开头的方法,在调用前后都会执行环绕通知around()方法内的逻

    @Component
    @Aspect
    @Slf4j
    public class ExampleAop {
        @Pointcut("execution(* com.fanfu.service..*.add*(String))")
        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;
        }
    }

    Spring Aop注意事项

    1、与定义的切点匹配方法,如果在当前调用链中,方法在当前类是首次匹配则会命中,即执行相关的通知,如果当前的调用链没有结束,又在当前方法里调用了当前类的与其他切入点匹配方法,则不会再命中,即其他与切入点匹配的方法执行的时候不会再触发相关的通知;

    如下示例:

    当请求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());
        }
    }

    Springboot项目如何快速实现Aop功能

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

    Springboot项目如何快速实现Aop功能

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    Java实现AOP日志记录配置与代码详解
    Java实现AOP日志记录配置与代码详解

    AOP日志记录通过将日志功能与业务逻辑解耦,使代码更清晰、维护更便捷。1.首先需引入SpringAOP和AspectJ依赖;2.定义切点(Pointcut)指定拦截方法;3.编写通知(Advice)处理日志逻辑,如@Before、@AfterReturning等;4.创建切面(Aspect)整合切点与通知,并标注@Aspect和@Component;5.启用AOP自动代理。敏感数据可通过脱敏、限制记录字段或调整日志级别来避免泄露。性能影响取决于日志复杂度,建议异步记录和优化操作。选择切点表达式时应结合方法

    Spring框架中 AOP(面向方面编程)是如何实现的?
    Spring框架中 AOP(面向方面编程)是如何实现的?

    SpringAOP基于Java动态代理来实现面向方面编程,允许在不修改原始代码情况下,在方法执行前后插入附加逻辑。具体步骤如下:创建代理对象,使用Proxy.newProxyInstance()方法,提供类加载器、接口和调用处理器。调用处理器的invoke()方法,获取目标对象、拦截器链,并依次调用拦截器invoke()。最后,如果没有异常,调用目标对象的方法。

    PHP中的面向切面编程(AOP)初探
    PHP中的面向切面编程(AOP)初探

    随着软件开发的不断发展,越来越多的编程语言和技术层出不穷。其中,PHP作为一门广泛应用于Web开发的脚本语言,在不断发展和革新的过程中,也涌现出了许多重要的概念和技术,如面向对象编程(OOP)、设计模式等,而今天我们要探讨的是PHP中的另一种编程方式——面向切面编程(AOP)。什么是AOP?AOP,全称是Aspect-OrientedProgramming

    springBoot项目常用目录有哪些
    springBoot项目常用目录有哪些

    springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

    探索Spring Boot架构:深入解析框架内部机制
    探索Spring Boot架构:深入解析框架内部机制

    SpringBoot的架构可以分为以下几个主要组件:Spring核心模块:springBoot的核心模块提供了Spring框架的基本功能,包括依赖注入、aop、事务管理等。这些模块是Spring应用程序的基础,为SpringBoot的开发提供了坚实的基础。SpringBootStarter:SpringBootStarter是一个预先配置好的依赖项集合,它可以帮助开发者快速构建Spring应用程序。SpringBootStarter提供了各种各样的依赖项,涵盖了各种常用的功能,如web开发、数据访问、安全

    解析SpringBoot和SpringMVC之间的异同
    解析SpringBoot和SpringMVC之间的异同

    解析SpringBoot和SpringMVC之间的异同SpringBoot和SpringMVC是Java领域中非常重要的开发框架。虽然它们都属于Spring框架的一部分,但是在使用和功能上有一些明显的区别。本文将对SpringBoot和SpringMVC进行比较,解析它们之间的异同。首先,让我们来了解一下SpringBoot。SpringBo

    深入了解Spring中AOP的典型应用方式
    深入了解Spring中AOP的典型应用方式

    探索Spring中AOP的常见应用方式概述:随着软件开发的不断发展,业务逻辑的复杂性也日益增加。为了提高代码的可维护性和重用性,以及实现切面关注点的分离,面向切面编程(AOP)的概念被引入到软件开发中。Spring框架是Java开发中广泛应用的框架之一,也提供了强大的AOP支持。本文将探索Spring中AOP的常见应用方式,并提供具体的代码示例。一、前置通知

    解析Spring AOP的常见应用场景
    解析Spring AOP的常见应用场景

    Spring中AOP的常见应用方式解析引言:在软件开发过程中,面向切面编程(AOP)是一种很重要的技术,它能够通过在程序运行期间动态地将特定的代码片段织入到目标方法中,提供额外的功能和扩展。而Spring作为一个强大的开发框架,提供了丰富的AOP支持,本文将详细介绍Spring中AOP的常见应用方式,包括声明式和编程式两种方式,并提供具体的代码示例。一、声明

    探索SpringCloud和SpringBoot在微服务领域的不同应用方式
    探索SpringCloud和SpringBoot在微服务领域的不同应用方式

    近年来,随着云计算和分布式架构的兴起,微服务架构的应用越来越广泛。而SpringCloud和SpringBoot作为Java开发中的两个重要框架,对于微服务的实现起到了重要的作用。然而,很多人对于它们在微服务领域的不同应用方式还存在一定的疑惑。本文将从不同的角度探索SpringCloud和SpringBoot在微服务中的应用方式。首先,我们来了解一下Spri

    SpringBoot和SpringMVC有何区别?
    SpringBoot和SpringMVC有何区别?

    SpringBoot与SpringMVC的不同之处在哪里?SpringBoot和SpringMVC是两个非常流行的Java开发框架,用于构建Web应用程序。尽管它们经常分别被使用,但它们之间的不同之处也是很明显的。首先,SpringBoot可以被看作是一个Spring框架的扩展或者增强版。它旨在简化Spring应用程序的初始化和配置过程,以帮助开发人

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。