对于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,代码如下:

public class ResultVO {

private Integer code;

private String message;

private Object data;

//getter and setter

}

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

@RequestMapping(value = "test")

@RestController

public class TestController {

@Resource

private TestService testService;

@GetMapping(value = "getResult")

public ResultVO getResult(ParamVO paramVO) {

ResultVO resultData = testService.getResultData(paramVO);

return resultData;

}

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

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

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

首先让工程引入AOP的包:

org.springframework.boot

spring-boot-starter-aop

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

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

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

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

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

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

@Pointcut(ExpGetResultDataPonit)

public void excuteService() {

}

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

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

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

@Override

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

Object result;

try {

//@Before

result = method.invoke(target, args);

//@After

return result;

} catch (InvocationTargetException e) {

Throwable targetException = e.getTargetException();

//@AfterThrowing

throw targetException;

} finally {

//@AfterReturning

}

}

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

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

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

@Before("excuteService()")

public void doBeforeMethod(JoinPoint joinPoint) {

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

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

Object[] obj = joinPoint.getArgs();

for (Object argItem : obj) {

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

if (argItem instanceof ParamVO) {

ParamVO paramVO = (ParamVO) argItem;

paramVO.setInputParam("666666");

}

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

}

}

/**

* 后置返回通知

* 这里需要注意的是:

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

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

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

*/

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

public void doAfterReturningAdvice1(JoinPoint joinPoint, Object keys) {

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

if (keys instanceof ResultVO) {

ResultVO resultVO = (ResultVO) keys;

String message = resultVO.getMessage();

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

}

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

}

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

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

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

/**

* 环绕通知:

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

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

*/

@Around(ExpGetResultDataPonit)

public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {

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

processInputArg(proceedingJoinPoint.getArgs());

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

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

processOutPutObj(obj);

return obj;

} catch (Throwable throwable) {

throwable.printStackTrace();

}

return null;

}

/**

* 处理返回对象

*/

private void processOutPutObj(Object obj) {

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

if (obj instanceof ResultVO) {

ResultVO resultVO = (ResultVO) obj;

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

System.out.println(resultVO);

}

}

/**

* 处理输入参数

*

* @param args 入参列表

*/

private void processInputArg(Object[] args) {

for (Object arg : args) {

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

if (arg instanceof ParamVO) {

ParamVO paramVO = (ParamVO) arg;

paramVO.setInputParam("654321");

}

}

}

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

切面代码完整如下:

package com.haiyang.onlinejava.complier.aspect;

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

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

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.springframework.context.annotation.Configuration;

@Configuration

@Aspect

public class ServiceAspect {

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

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

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

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

@Pointcut(ExpGetResultDataPonit)

public void excuteService() {

}

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

// @Before("excuteService()")

public void doBeforeMethod(JoinPoint joinPoint) {

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

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

Object[] obj = joinPoint.getArgs();

for (Object argItem : obj) {

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

if (argItem instanceof ParamVO) {

ParamVO paramVO = (ParamVO) argItem;

paramVO.setInputParam("666666");

}

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

}

}

/**

* 后置返回通知

* 这里需要注意的是:

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

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

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

*/

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

public void doAfterReturningAdvice1(JoinPoint joinPoint, Object keys) {

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

if (keys instanceof ResultVO) {

ResultVO resultVO = (ResultVO) keys;

String message = resultVO.getMessage();

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

}

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

}

/**

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

*/

// @After("excuteService()")

public void doAfterAdvice(JoinPoint joinPoint) {

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

}

/**

* 环绕通知:

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

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

*/

@Around(ExpGetResultDataPonit)

public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {

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

processInputArg(proceedingJoinPoint.getArgs());

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

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

processOutPutObj(obj);

return obj;

} catch (Throwable throwable) {

throwable.printStackTrace();

}

return null;

}

/**

* 处理返回对象

*/

private void processOutPutObj(Object obj) {

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

if (obj instanceof ResultVO) {

ResultVO resultVO = (ResultVO) obj;

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

System.out.println(resultVO);

}

}

/**

* 处理输入参数

*

* @param args 入参列表

*/

private void processInputArg(Object[] args) {

for (Object arg : args) {

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

if (arg instanceof ParamVO) {

ParamVO paramVO = (ParamVO) arg;

paramVO.setInputParam("654321");

}

}

}

}

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

控制台打印的日志为:

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

Around

AroundBefore

before

method.invoke()

AroundAfter

After

AfterReturning

aop对请求后端的参数修改_Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理...相关推荐

  1. aop对请求后端的参数修改_Spring Aop 修改目标方法参数和返回值

    @Component("changeIdNoAopHandler")public classChangeIdNoAopHandler {private static Logger ...

  2. @scheduled cron动态修改_spring boot实现动态增删启停定时任务

    作者:jessehua 来源:https://www.jianshu.com/p/0f68936393fd 在spring boot项目中,可以通过@EnableScheduling注解和@Sched ...

  3. springboot显示信息并且修改_Spring Boot小结-03--增.删.改.查

    将数据库商品数据进行-增.删.改.查 一.创建项目并添加依赖 *创建项目并设置基本信息 *指定项目核心依赖 *项目结构 *项目配置文件 二.业务实现 *Pojo类定义 *Dao接口方法及映射定义pac ...

  4. 11.2.0.4rac service_name参数修改

    环境介绍 1)客户环境11.2.0.4 两节点 rac,集群重启后,集群资源一切正常,应用cs架构,连接数据库报错,提示连接对象不存在2)分析报错原因,连接数据库方式:ip:Port/service_ ...

  5. 400是什么错误_Spring Boot的REST API错误处理

    Spring Boot提供了一个非常棒的开箱即用的异常处理机制.errorcontroller的默认实现在捕获和处理异常方面做得很好.此外,我们还可以定义自己的@ExceptionHandler s来 ...

  6. 前端参数无法转为后端实体内部类_Spring Boot返回前端Long型丢失精度

    最近为Prong开发了一个基于snowflake算法的Java分布式ID组件,将实体主键从原来的String类型的UUID修改成了Long型的分布式ID.修改后发现前端显示的ID和数据库中的ID不一致 ...

  7. Vue简单封装axios—解决post请求后端接收不到参数问题

    1.在src/下新建api文件夹,api/下新建index.js和public.js 在public.js中: import axios from 'axios'; import qs from 'q ...

  8. 【跨域问题】Vue简单封装axios—解决post请求后端接收不到参数问题

    原因分析: 1. 传参数据没有序列化? 解决办法: [推荐] [推荐阅读] vue+axios+qs序列化 "三步解析"[含demo实例]- 代码篇 2. 服务端接受数据格式参数配 ...

  9. Get请求后端并带参数

    一.Get请求后端并带参数 JS请求后端并携带参数 业务场景:前端页面导出Excel文件 业务要求:后端生成文件,并记录数据 前端:exportData() {var data = JSON.stri ...

最新文章

  1. Android 中文API (94) —— MediaController
  2. ad域帐号登录提示无法处理请求_微软Windows Server之AD域控制器迁移测试方案
  3. oracle =1,oracle中的 where 1=1 和where 1 !=1
  4. 英文简历 计算机知识,计算机应届生英文简历范文
  5. hsi i均衡化 java_基于HSI-mod的直方图均衡化
  6. 启动Samples-Web-Start Web Server时,提示Could not open port 1080
  7. 2021-06-21层次选择器
  8. ajax id sort,带有ajax更新的Jqueryui可排序列表
  9. 计算机教师所需技能,信息技术教师应具备哪些教学技能
  10. 本科毕业论文论文框架,可参考
  11. 叉乘应用:判断三角形方向正反/三个点顺时针逆时针
  12. 计算机绘图中有六种方法绘圆,绘图用品和制图方法
  13. Android使用Activity用作弹出式对话框Dialog
  14. 【编程工具】——pycharm
  15. 平台软件每日构建总结
  16. 关于物联网进入元宇宙时代的基础与发展思考
  17. Python文本彩色图像去污
  18. 英文网页批量翻译导出本地教程
  19. 教你一招H5快应用快速回到首页
  20. python程序只能使用源代码进行运行、不能打包_中国大学MOOC计算机程序设计语言(Python)网课答案...

热门文章

  1. python netsnmp_使用 Net-SNMP 和 IPython
  2. 二极管7种应用电路详解之四
  3. python找出函数最小值极其对应的自变量的值
  4. pywebio和stylecolud 做的词云gui
  5. Pytorch-基于Transformer的情感分类
  6. JS学习系列08 - 内存分配
  7. 关于Vue中常用的工具函数封装
  8. 强大的Vivado IP工具——自定义IP的使用
  9. Project Euler 50 Consecutive prime sum
  10. 大数据时代的网络视频营销