Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理

Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理

本文链接:https://blog.csdn.net/puhaiyang/article/details/78146620

本文链接:https://blog.csdn.net/puhaiyang/article/details/78146620

spring Aop切面中的@Before @Around等执行顺序与请求参数统一解码

https://www.cnblogs.com/newAndHui/p/11771035.html

对于spring框架来说,最重要的两大特性就是AOP 和IOC。

以前一直都知道有这两个东西,在平时做的项目中也常常会涉及到这两块,像spring的事务管理什么的,在看了些源码后,才知道原来事务管理也是用的AOP来实现的。对于IOC的话,平时接触的就更多了,什么autowired,resource各种注解,就是IOC的各种应用。

一直我也想着能有机会自己动手写个aop的小DEMO,不过一直没机会,想到了许多,在网上一搜,基本上都已经有了。今天想到一个用于对service方法进行拦截的功能点,今天决定用springBoot的工程来实现一下。

功能点描述:对某个service的方法执行前,获取出入参,对入参的参数进行修改,将参数进行替换。然后在这个方法执行完毕后,再对其返回结果进行修改。主要就是对一个方法装饰一下。说到装饰,第一想到的是采用装饰器模式来实现,但装饰器模式需要对整个代码的结构进行一些修改,为了达到对以前的代码不进行任何接触,且装饰器模式的局限性较小,所以最好还是用spring的AOP来实现这种对代码无任何侵入的功能。

service的代码如下:

@Service

public class TestServiceImpl implements TestService {

private Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

public ResultVO getResultData(ParamVO paramVO) {

return process(paramVO);

}

private ResultVO process(ParamVO paramVO) {

logger.info("----->input INFO:{}", paramVO);

ResultVO resultVO = new ResultVO();

resultVO.setCode(200);

resultVO.setData(Arrays.asList("123", "456", "789"));

resultVO.setMessage("OK!!!!!!!! and your inputParam is" + paramVO.toString());

logger.info("---->return INFO:{}", resultVO.toString());

return resultVO;

}

其中入参为paramVO,代码如下:

public class ParamVO {

private String inputParam;

private String inputParam2;

//getter and setter

}

返回的参数ResutVO,代码如下:

  1. public class ResultVO {

  2.  

    private Integer code;

  3.  

    private String message;

  4.  

    private Object data;

  5.  

    //getter and setter

  6.  

    }

其调用的入口为一个controller,代码如下:

  1. @RequestMapping(value = "test")

  2.  

    @RestController

  3.  

    public class TestController {

  4.  

    @Resource

  5.  

    private TestService testService;

  6.  

  7.  

    @GetMapping(value = "getResult")

  8.  

    public ResultVO getResult(ParamVO paramVO) {

  9.  

    ResultVO resultData = testService.getResultData(paramVO);

  10.  

    return resultData;

  11.  

    }

在正常情况下,按照如上的代码进行调用将返回如下的信息:

通过返回的信息可以看到,入参是我们在请求参数传入的inputParam=111和inputParam2=2220

现在要做的就是把入参的参数通过AOP来拦截,并进行修改。对于返回值,也进行一下修改。

首先让工程引入AOP的包:

  1. <!-- AOP -->

  2.  

    <dependency>

  3.  

    <groupId>org.springframework.boot</groupId>

  4.  

    <artifactId>spring-boot-starter-aop</artifactId>

  5.  

    </dependency>

然后定义一个Aspect,并指定一个切入点,配置要进行哪些方法的拦截

这里只针对TestSevice这个接口下的getResultData进行拦截

  1. private final String ExpGetResultDataPonit = "execution(* com.haiyang.onlinejava.complier.service.TestService.getResultData(..))";

  2.  

  3.  

    //定义切入点,拦截servie包其子包下的所有类的所有方法

  4.  

    // @Pointcut("execution(* com.haiyang.onlinejava.complier.service..*.*(..))")

  5.  

    //拦截指定的方法,这里指只拦截TestService.getResultData这个方法

  6.  

    @Pointcut(ExpGetResultDataPonit)

  7.  

    public void excuteService() {

  8.  

  9.  

    }

对于切入点的配置表达式,可以在网上自行搜索,网上也有许多

在指定了切入点后,就可以对这个切入点excuteService()这个点进行相应的操作了。

可以配置@Before  @After 等来进行相应的处理,其代表的意思分别是前置与后置,就是下面代码这个意思

  1. @Override

  2.  

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  3.  

    Object result;

  4.  

    try {

  5.  

    //@Before

  6.  

    result = method.invoke(target, args);

  7.  

    //@After

  8.  

    return result;

  9.  

    } catch (InvocationTargetException e) {

  10.  

    Throwable targetException = e.getTargetException();

  11.  

    //@AfterThrowing

  12.  

    throw targetException;

  13.  

    } finally {

  14.  

    //@AfterReturning

  15.  

    }

  16.  

    }

由于要对入参和最终返回结果进行处理,所以选择Before和AfterReturning,原来以为after也可以,但看了下,它好像并不能拿到这个方法的返回值,而AfterReturning是一定可以的

拦截后,对应的处理代码如下:

  1. //执行方法前的拦截方法

  2.  

    @Before("excuteService()")

  3.  

    public void doBeforeMethod(JoinPoint joinPoint) {

  4.  

    System.out.println("我是前置通知,我将要执行一个方法了");

  5.  

    //获取目标方法的参数信息

  6.  

    Object[] obj = joinPoint.getArgs();

  7.  

    for (Object argItem : obj) {

  8.  

    System.out.println("---->now-->argItem:" + argItem);

  9.  

    if (argItem instanceof ParamVO) {

  10.  

    ParamVO paramVO = (ParamVO) argItem;

  11.  

    paramVO.setInputParam("666666");

  12.  

    }

  13.  

    System.out.println("---->after-->argItem:" + argItem);

  14.  

    }

  15.  

    }

  16.  

  17.  

    /**

  18.  

    * 后置返回通知

  19.  

    * 这里需要注意的是:

  20.  

    * 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息

  21.  

    * 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数

  22.  

    * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值

  23.  

    */

  24.  

    @AfterReturning(value = ExpGetResultDataPonit, returning = "keys")

  25.  

    public void doAfterReturningAdvice1(JoinPoint joinPoint, Object keys) {

  26.  

    System.out.println("第一个后置返回通知的返回值:" + keys);

  27.  

    if (keys instanceof ResultVO) {

  28.  

    ResultVO resultVO = (ResultVO) keys;

  29.  

    String message = resultVO.getMessage();

  30.  

    resultVO.setMessage("通过AOP把值修改了 " + message);

  31.  

    }

  32.  

    System.out.println("修改完毕-->返回方法为:" + keys);

  33.  

    }

然后再请求一下之前的请求

从这里可以看出,通过AOP的拦截,已经把对应的值修改了,入参inputParam由111改成了666666,返回结果message也加上了几个字

除了用Before和AfterReturning外,还可以用环绕来实现同样的功能,如:

  1. /**

  2.  

    * 环绕通知:

  3.  

    * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。

  4.  

    * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型

  5.  

    */

  6.  

    @Around(ExpGetResultDataPonit)

  7.  

    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {

  8.  

    System.out.println("环绕通知的目标方法名:" + proceedingJoinPoint.getSignature().getName());

  9.  

    processInputArg(proceedingJoinPoint.getArgs());

  10.  

    try {//obj之前可以写目标方法执行前的逻辑

  11.  

    Object obj = proceedingJoinPoint.proceed();//调用执行目标方法

  12.  

    processOutPutObj(obj);

  13.  

    return obj;

  14.  

    } catch (Throwable throwable) {

  15.  

    throwable.printStackTrace();

  16.  

    }

  17.  

    return null;

  18.  

    }

  19.  

  20.  

    /**

  21.  

    * 处理返回对象

  22.  

    */

  23.  

    private void processOutPutObj(Object obj) {

  24.  

    System.out.println("OBJ 原本为:" + obj.toString());

  25.  

    if (obj instanceof ResultVO) {

  26.  

    ResultVO resultVO = (ResultVO) obj;

  27.  

    resultVO.setMessage("哈哈,我把值修改了" + resultVO.getMessage());

  28.  

    System.out.println(resultVO);

  29.  

    }

  30.  

    }

  31.  

  32.  

    /**

  33.  

    * 处理输入参数

  34.  

    *

  35.  

    * @param args 入参列表

  36.  

    */

  37.  

    private void processInputArg(Object[] args) {

  38.  

    for (Object arg : args) {

  39.  

    System.out.println("ARG原来为:" + arg);

  40.  

    if (arg instanceof ParamVO) {

  41.  

    ParamVO paramVO = (ParamVO) arg;

  42.  

    paramVO.setInputParam("654321");

  43.  

    }

  44.  

    }

  45.  

    }

这样写,也可以达到相同的目的

切面代码完整如下:

  1. package com.haiyang.onlinejava.complier.aspect;

  2.  

  3.  

    import com.haiyang.onlinejava.complier.vo.ParamVO;

  4.  

    import com.haiyang.onlinejava.complier.vo.ResultVO;

  5.  

    import org.aspectj.lang.JoinPoint;

  6.  

    import org.aspectj.lang.ProceedingJoinPoint;

  7.  

    import org.aspectj.lang.annotation.*;

  8.  

    import org.springframework.context.annotation.Configuration;

  9.  

  10.  

    @Configuration

  11.  

    @Aspect

  12.  

    public class ServiceAspect {

  13.  

  14.  

    private final String ExpGetResultDataPonit = "execution(* com.haiyang.onlinejava.complier.service.TestService.getResultData(..))";

  15.  

  16.  

    //定义切入点,拦截servie包其子包下的所有类的所有方法

  17.  

    // @Pointcut("execution(* com.haiyang.onlinejava.complier.service..*.*(..))")

  18.  

    //拦截指定的方法,这里指只拦截TestService.getResultData这个方法

  19.  

    @Pointcut(ExpGetResultDataPonit)

  20.  

    public void excuteService() {

  21.  

  22.  

    }

  23.  

  24.  

    //执行方法前的拦截方法

  25.  

    // @Before("excuteService()")

  26.  

    public void doBeforeMethod(JoinPoint joinPoint) {

  27.  

    System.out.println("我是前置通知,我将要执行一个方法了");

  28.  

    //获取目标方法的参数信息

  29.  

    Object[] obj = joinPoint.getArgs();

  30.  

    for (Object argItem : obj) {

  31.  

    System.out.println("---->now-->argItem:" + argItem);

  32.  

    if (argItem instanceof ParamVO) {

  33.  

    ParamVO paramVO = (ParamVO) argItem;

  34.  

    paramVO.setInputParam("666666");

  35.  

    }

  36.  

    System.out.println("---->after-->argItem:" + argItem);

  37.  

    }

  38.  

    }

  39.  

  40.  

    /**

  41.  

    * 后置返回通知

  42.  

    * 这里需要注意的是:

  43.  

    * 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息

  44.  

    * 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数

  45.  

    * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值

  46.  

    */

  47.  

    // @AfterReturning(value = ExpGetResultDataPonit, returning = "keys")

  48.  

    public void doAfterReturningAdvice1(JoinPoint joinPoint, Object keys) {

  49.  

    System.out.println("第一个后置返回通知的返回值:" + keys);

  50.  

    if (keys instanceof ResultVO) {

  51.  

    ResultVO resultVO = (ResultVO) keys;

  52.  

    String message = resultVO.getMessage();

  53.  

    resultVO.setMessage("通过AOP把值修改了 " + message);

  54.  

    }

  55.  

    System.out.println("修改完毕-->返回方法为:" + keys);

  56.  

    }

  57.  

  58.  

  59.  

    /**

  60.  

    * 后置最终通知(目标方法只要执行完了就会执行后置通知方法)

  61.  

    */

  62.  

    // @After("excuteService()")

  63.  

    public void doAfterAdvice(JoinPoint joinPoint) {

  64.  

  65.  

    System.out.println("后置通知执行了!!!!");

  66.  

    }

  67.  

  68.  

  69.  

  70.  

  71.  

    /**

  72.  

    * 环绕通知:

  73.  

    * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。

  74.  

    * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型

  75.  

    */

  76.  

    @Around(ExpGetResultDataPonit)

  77.  

    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {

  78.  

    System.out.println("环绕通知的目标方法名:" + proceedingJoinPoint.getSignature().getName());

  79.  

    processInputArg(proceedingJoinPoint.getArgs());

  80.  

    try {//obj之前可以写目标方法执行前的逻辑

  81.  

    Object obj = proceedingJoinPoint.proceed();//调用执行目标方法

  82.  

    processOutPutObj(obj);

  83.  

    return obj;

  84.  

    } catch (Throwable throwable) {

  85.  

    throwable.printStackTrace();

  86.  

    }

  87.  

    return null;

  88.  

    }

  89.  

  90.  

    /**

  91.  

    * 处理返回对象

  92.  

    */

  93.  

    private void processOutPutObj(Object obj) {

  94.  

    System.out.println("OBJ 原本为:" + obj.toString());

  95.  

    if (obj instanceof ResultVO) {

  96.  

    ResultVO resultVO = (ResultVO) obj;

  97.  

    resultVO.setMessage("哈哈,我把值修改了" + resultVO.getMessage());

  98.  

    System.out.println(resultVO);

  99.  

    }

  100.  

    }

  101.  

  102.  

    /**

  103.  

    * 处理输入参数

  104.  

    *

  105.  

    * @param args 入参列表

  106.  

    */

  107.  

    private void processInputArg(Object[] args) {

  108.  

    for (Object arg : args) {

  109.  

    System.out.println("ARG原来为:" + arg);

  110.  

    if (arg instanceof ParamVO) {

  111.  

    ParamVO paramVO = (ParamVO) arg;

  112.  

    paramVO.setInputParam("654321");

  113.  

    }

  114.  

    }

  115.  

    }

  116.  

  117.  

  118.  

    }

如不进行@Before和@AfterReturing的注释,最终的结果如下:

控制台打印的日志为:

通过查看打印的结果,我们可以知道@Around @Before  @After  @AfterReturning这几个注解的执行顺序为:

Around
AroundBefore
  before
  method.invoke()
  AroundAfter
      After
AfterReturning

主要参考:http://blog.csdn.net/zknxx/article/details/53240959

原文地址:https://www.cnblogs.com/kelelipeng/p/11993753.html

时间: 2024-08-29 04:48:19

Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理的相关文章

spring boot aop打印http请求回复日志包含请求体

一.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>

spring boot中使用xml请求

spring boot 中使用xml请求 先在pom文件中引入依赖包 <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.9.0</version></dependency> 编写请求实体 package com.sc

Spring Boot AOP解析

Spring Boot AOP 面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. AOP(Aspect Oriented Program) 面向切面编程 在面向切面编程的思想里面,把功能分为核心业务功能和周边功能. 核心业务,比如登陆,增加数据,删除数据都叫核心业务 周边功能,比如性能统计,日志,事务管理等等 周边功能在 Spring Boot 的面向切面编程AOP思想里,即被定义为切面 在面向切

spring boot aop

这里要做就是利用aop 来打印请求的日志,返回结果,简单的异常处理 一:使用@Aspect注解创建切面,类名是 RquestAspect import javax.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.as

Spring Boot + Vue 跨域请求问题

使用Spring Boot + Vue 做前后端分离项目搭建,实现登录时,出现跨域请求 Access to XMLHttpRequest at 'http://localhost/open/login' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. V

Spring Boot AOP 简易操作日志管理

AOP (Aspect Oriented Programming) 面向切面编程. 业务有核心业务和边缘业务. 比如用户管理,菜单管理,权限管理,这些都属于核心业务. 比如日志管理,操作记录管理,这些都是边缘业务,可以统一的提出来. 尝试使用SpringBoot +AOP 提出操作记录业务. github aop_demo package com.lick.aspect.lang.annotation; import com.lick.aspect.lang.enums.BusinessType

Spring Boot AOP Demo

什么是AOP? AOP面向切面,切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性. 实现策略JAVA SE动态代理 CGLib 相关注解 @Aspect(方面) @Pointcut(切入点) @Before(之前) @After(之后) pom.xml <!--引用AOP--> <dependency> <groupId>org.springframework.boot</gro

spring boot aop日志管理(MongoDB)

aop拦截的是controller层请求,正常的请求用@Before来拦截, 异常的请求用@AfterThrowing来拦截 1.引用aop jar包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.0.3.RELEASE</version>

spring cloud zipkin sleuth与spring boot aop结合后,启动慢

问题描述: 引入了spring cloud的监控starter,间接引入jooq. 又引入了 spring-boot-starter-web,所以间接引入aop. 参考下面资料后:https://github.com/jOOQ/jOOQ/issues/5902 发现可以通过修改aop表达式解决. 于是修改aop表达式如下: 问题解决. 原文地址:https://www.cnblogs.com/grey-wolf/p/8296026.html