文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot中如何使用Aop

2023-06-20 18:46

关注

这篇文章将为大家详细讲解有关SpringBoot中如何使用Aop,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

什么是aop

AOP(Aspect OrientedProgramming):面向切面编程,面向切面编程(也叫面向方面编程),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

使用场景

利用AOP可以对我们边缘业务进行隔离,降低无关业务逻辑耦合性。提高程序的可重用性,同时提高了开发的效率。一般用于日志记录,性能统计,安全控制,权限管理,事务处理,异常处理,资源池管理。使用场景

为什么需要面向切面编程

面向对象编程(OOP)的好处是显而易见的,缺点也同样明显。当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录、性能监控等,如果采用面向对象编程的方法,需要在每个对象里面都添加相同的方法,这样就产生了较大的重复工作量和大量的重复代码,不利于维护。面向切面编程(AOP)是面向对象编程的补充,简单来说就是统一处理某一“切面”的问题的编程思想。如果使用AOP的方式进行日志的记录和处理,所有的日志代码都集中于一处,不需要再每个方法里面都去添加,极大减少了重复代码。

技术要点

整合使用

导入依赖

在springboot中使用aop要导aop依赖

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

注意这里版本依赖于spring-boot-start-parent父pom中的spring-boot-dependencies

编写拦截的bean

这里我们定义一个controller用于拦截所有请求的记录

@RestControllerpublic class AopController {    @RequestMapping("/hello")    public String sayHello(){        System.out.println("hello");        return "hello";    }}

定义切面

SpringBoot在使用切面的时候采用@Aspect注解对POJO进行标注,该注解表明该类不仅仅是一个POJO,还是一个切面容器

定义切点

切点是通过@Pointcut注解和切点表达式定义的。

@Pointcut注解可以在一个切面内定义可重用的切点。

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且实际中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如图是execution表达式的语法:

SpringBoot中如何使用Aop

execution表示在方法执行的时候触发。以“”开头,表明方法返回值类型为任意类型。然后是全限定的类名和方法名,“”可以表示任意类和任意方法。对于方法参数列表,可以使用“..”表示参数为任意类型。如果需要多个表达式,可以使用“&&”、“||”和“!”完成与、或、非的操作。

定义通知

通知有五种类型,分别是:

代码中定义了三种类型的通知,使用@Before注解标识前置通知,打印“beforeAdvice...”,使用@After注解标识后置通知,打印“AfterAdvice...”,使用@Around注解标识环绕通知,在方法执行前和执行之后分别打印“before”和“after”。这样一个切面就定义好了,代码如下:

@Aspect@Componentpublic class AopAdvice {    @Pointcut("execution (* com.shangguan.aop.controller.*.*(..))")    public void test() {    }    @Before("test()")    public void beforeAdvice() {        System.out.println("beforeAdvice...");    }    @After("test()")    public void afterAdvice() {        System.out.println("afterAdvice...");    }    @Around("test()")    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {        System.out.println("before");        try {            proceedingJoinPoint.proceed();        } catch (Throwable t) {            t.printStackTrace();        }        System.out.println("after");    }}

运行结果

SpringBoot中如何使用Aop

案例场景

这里我们通过一个日志记录场景来完整的使用Aop切面业务层只需关心代码逻辑实现而不用关心请求参数和响应参数的日志记录

那么首先我们需要自定义一个全局日志记录的切面类GlobalLogAspect

然后在该类添加@Aspect注解,然后在定义一个公共的切入点(Pointcut),指向需要处理的包,然后在定义一个前置通知(添加@Before注解),后置通知(添加@AfterReturning)和环绕通知(添加@Around)方法实现即可

日志信息类

package cn.soboys.core;import lombok.Data;@Datapublic class LogSubject {        private String description;        private String username;        private String startTime;        private String spendTime;        private String url;        private String method;        private String ip;        private Object parameter;        private Object result;        private String city;        private String device;}

全局日志拦截

package cn.soboys.core;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.reflect.MethodSignature;import java.lang.reflect.Method;public class BaseAspectSupport {    public Method resolveMethod(ProceedingJoinPoint point) {        MethodSignature signature = (MethodSignature)point.getSignature();        Class<?> targetClass = point.getTarget().getClass();        Method method = getDeclaredMethod(targetClass, signature.getName(),                signature.getMethod().getParameterTypes());        if (method == null) {            throw new IllegalStateException("无法解析目标方法: " + signature.getMethod().getName());        }        return method;    }    private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {        try {            return clazz.getDeclaredMethod(name, parameterTypes);        } catch (NoSuchMethodException e) {            Class<?> superClass = clazz.getSuperclass();            if (superClass != null) {                return getDeclaredMethod(superClass, name, parameterTypes);            }        }        return null;    }}

GlobalLogAspect

package cn.soboys.core;import cn.hutool.core.date.DateUtil;import cn.hutool.core.date.TimeInterval;import cn.hutool.json.JSONUtil;import cn.soboys.core.utils.HttpContextUtil;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestParam;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;@Slf4j@Aspect@Componentpublic class GlobalLogAspect extends BaseAspectSupport {        @Pointcut("execution(public * cn.soboys.mallapi.controller.*.*(..))")    public void log() {    }        @Around("log()")    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {        LogSubject logSubject = new LogSubject();        //记录时间定时器        TimeInterval timer = DateUtil.timer(true);        //执行结果        Object result = joinPoint.proceed();        logSubject.setResult(result);        //执行消耗时间        String endTime = timer.intervalPretty();        logSubject.setSpendTime(endTime);        //执行参数        Method method = resolveMethod(joinPoint);        logSubject.setParameter(getParameter(method, joinPoint.getArgs()));        HttpServletRequest request = HttpContextUtil.getRequest();        // 接口请求时间        logSubject.setStartTime(DateUtil.now());        //请求链接        logSubject.setUrl(request.getRequestURL().toString());        //请求方法GET,POST等        logSubject.setMethod(request.getMethod());        //请求设备信息        logSubject.setDevice(HttpContextUtil.getDevice());        //请求地址        logSubject.setIp(HttpContextUtil.getIpAddr());        //接口描述        if (method.isAnnotationPresent(ApiOperation.class)) {            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);            logSubject.setDescription(apiOperation.value());        }        String a = JSONUtil.toJsonPrettyStr(logSubject);        log.info(a);        return result;    }        private Object getParameter(Method method, Object[] args) {        List<Object> argList = new ArrayList<>();        Parameter[] parameters = method.getParameters();        Map<String, Object> map = new HashMap<>();        for (int i = 0; i < parameters.length; i++) {            //将RequestBody注解修饰的参数作为请求参数            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);            //将RequestParam注解修饰的参数作为请求参数            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);            String key = parameters[i].getName();            if (requestBody != null) {                argList.add(args[i]);            } else if (requestParam != null) {                map.put(key, args[i]);            } else {                map.put(key, args[i]);            }        }        if (map.size() > 0) {            argList.add(map);        }        if (argList.size() == 0) {            return null;        } else if (argList.size() == 1) {            return argList.get(0);        } else {            return argList;        }    }}

关于SpringBoot中如何使用Aop就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯