Spring AOP项目应用——方法入参校验 & 日志横切
转载:https://blog.csdn.net/Daybreak1209/article/details/80591566
应用一:方法入参校验
由于系统多个方法入参均对外封装了统一的Dto,其中Dto中几个必传参数在每个方法中都会进行相同的校验逻辑。笔者考虑采用Spring AOP进行优化,拦截方法进行参数校验。测试case实现如下:
Before
- /**
- * 被代理的目标类
- */
- @Service
- public class PayOrderTarget {
- @Autowired
- private PaymentOrderService paymentOrderService;
- public Result testQuery(QueryPaymentDto paymentOrderDto){
- PaymentOrderDto paymentOrderDto1 = paymentOrderService.queryPaymentOrder(paymentOrderDto);
- return ResultWrapper.success(paymentOrderDto1);
- }
- }
- /**
- * 通知类,横切逻辑
- */
- @Component
- @Aspect
- public class Advices {
- @Before("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")
- public Result before(JoinPoint proceedingJoinPoint) {
- // 拦截获取入参
- Object[] args = proceedingJoinPoint.getArgs();
- // 入参校验
- if (args ==null){
- return ResultWrapper.fail();
- }
- String input = JSON.toJSON(args).toString();
- Map<String, String> map = Splitter.on(",").withKeyValueSeparator(":").split(input);
- if (map.containsKey("businessId") || map.containsKey("payOrderId")){
- System.out.println("key null");
- return ResultWrapper.fail();
- }
- if (map.get("businessId")==null || map.get("payOrderId")==null ){
- System.out.println("value null");
- return ResultWrapper.fail();
- }
- System.out.println("----------前置通知----------");
- System.out.println(proceedingJoinPoint.getSignature().getName());
- return ResultWrapper.success();
- }
- }
测试类正常调用查询方法
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = {"classpath*:spring/spring-context.xml"})
- public class Test {
- @Autowired
- private PayOrderTarget payOrderTarget;
- @org.junit.Test
- public void test(){
- QueryPaymentDto paymentOrderDto=new QueryPaymentDto();
- paymentOrderDto.setPayOrderId(11l);
- paymentOrderDto.setBusinessId(112L);
- paymentOrderDto.setPageSize(1);
- payOrderTarget.testQuery(paymentOrderDto);
- }
- }
执行结果可对入参Dto进行拦截,执行before中的校验逻辑。但即便return fail之后,目标方法还是会被执行到。笔者是想实现参数校验失败,则直接返回,不执行接下来查询db的操作。此时则需要使用Around切入。
Around
- @Around("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")
- public Result around(ProceedingJoinPoint proceedingJoinPoint) {
- // 获取java数组
- Object[] args = proceedingJoinPoint.getArgs();
- JSONArray jsonArray = JSONArray.parseArray(JSONArray.toJSONString(args));
- String businessId = null;
- String payOrderId = null;
- for (int i = 0; i < jsonArray.size(); i++) {
- businessId = jsonArray.getJSONObject(0).getString("businessId");
- payOrderId = jsonArray.getJSONObject(0).getString("payOrderId");
- }
- ;
- //参数校验
- if (businessId == null || payOrderId == null) {
- System.out.println("value null");
- return ResultWrapper.fail();
- }
- //执行目标方法
- try {
- proceedingJoinPoint.proceed();
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- }
- System.out.println("----------Around通知----------");
- return ResultWrapper.success();
- }
简单介绍下,before和after切入都是接收JoinPoint对象,该对象可获取切点(即被代理对象)的入参、方法名等数据。
| 方法名 | 功能 |
|---|---|
| Signature getSignature(); | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
| Object[] getArgs(); | 获取传入目标方法的参数对象 |
| Object getTarget(); | 获取被代理的对象 |
| Object getThis(); | 获取代理对象 |
而Around接收ProceedingJoinPoint该接口继承自JoinPoint,新增了如下两个方法,通过调用proceedingJoinPoint.proceed方法才控制目标方法的调用。则在如上around中,进行参数校验,不合法则return,未进入到proceedingJoinPoint.proceed()处,达到方法不合规直接返回不调用查询逻辑。
| 方法名 | 功能 |
|---|---|
| Object proceed() throws Throwable | 执行目标方法 |
| Object proceed(Object[] var1) throws Throwable | 传入的新的参数去执行目标方法 |
注:本case采用注解声明,直接可运行。xml中增加如下配置(支持自动装配@Aspect注解的bean)即可。
<aop:aspectj-autoproxy proxy-target-class="true"/>
应用二:日志处理
实现将日志打印抽取,成为一个公共类,切入到目标类的方法入口、出口处打印方法名+参数信息;这个case重点引用了几种不同的AOP增强方式,简单介绍如下:
| Before | 在方法被调用之前调用通知 |
|---|---|
| Around | 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为 |
| After | 在方法完成之后调用通知,无论方法执行是否成功 |
| After-returning | 在方法返回结果后执行通知 |
| After-throwing | 在方法抛出异常后调用通知 |
- /**
- * login接口类 被代理接口类
- */
- public interface ILoginService {
- boolean login(String userName, String password);
- void quary() throws Exception;
- }
- /**
- * login实现类,被代理目标类
- */
- @Service
- public class LoginServiceImpl implements ILoginService {
- public boolean login(String userName, String password) {
- System.out.println("login:" + userName + "," + password);
- return true;
- }
- @Override
- public void quary() throws Exception{
- System.out.println("******quary*******");
- // 测试方法抛出异常后,解注After-throwing配置,会执行logArg切入,不抛异常不执行切点
- // int i=10/0;
- }
- }
日志包装类,将各类日志情况进行方法封装
- /**
- * 日志处理类
- */
- public interface ILogService {
- //无参的日志方法
- void log();
- //有参的日志方法
- void logArg(JoinPoint point);
- //有参有返回值的方法
- void logArgAndReturn(JoinPoint point, Object returnObj);
- }
- /**
- * 日志实现类
- */
- @Component("logService")
- @Aspect
- public class LogServiceImpl implements ILogService {
- @Override
- public void log() {
- System.out.println("*************Log*******************");
- }
- @Override
- public void logArg(JoinPoint point) {
- System.out.println("方法:"+point.getSignature().getName());
- Object[] args = point.getArgs();
- System.out.println("目标参数列表:");
- if (args != null) {
- for (Object obj : args) {
- System.out.println(obj + ",");
- }
- }
- }
- @Override
- public void logArgAndReturn(JoinPoint point, Object returnObj) {
- //此方法返回的是一个数组,数组中包括request以及ActionCofig等类对象
- Object[] args = point.getArgs();
- System.out.println("目标参数列表:");
- if (args != null) {
- for (Object obj : args) {
- System.out.println(obj + ",");
- }
- System.out.println("执行结果是:" + returnObj);
- }
- }
- }
配置(注释着重看下):
- <aop:config>
- <!-- 切入点 LoginServiceImpl类中所有方法都会被拦截,执行增强方法-->
- <aop:pointcut expression="execution(* com.payment.util.aoplogger.LoginServiceImpl.*(..))" id="myPointcut" />
- <!-- 切面-->
- <aop:aspect id="dd" ref="logService">
- <!-- LoginServiceImpl类方法执行前,增强执行日志service的log方法-->
- <!--<aop:before method="log" pointcut-ref="myPointcut" />-->
- <!-- LoginServiceImpl类方法执行后,增强执行日志service的logArg方法-->
- <!--<aop:after method="logArg" pointcut-ref="myPointcut"/>-->
- <!-- LoginServiceImpl类方法执行抛异常后,增强执行日志service的logArg方法-->
- <aop:after-throwing method="logArg" pointcut-ref="myPointcut"/>
- <!--<aop:after-returning method="logArgAndReturn" returning="returnObj" pointcut-ref="myPointcut"/>-->
- </aop:aspect>
- </aop:config>
注意:采用xml配置与使用@注解二选一(都写上即执行两次增强方法),对等关系如下:
|
<aop:aspect ref="advices"> |
@Aspect public class Advices |
|
<aop:pointcut expression="execution(* com.payment.util.springaop.PayOrderTarget.*(..))" id="pointcut1"/> <aop:before method="before" pointcut-ref="pointcut1"/> |
@Before("execution(* com.payment.util.springaop.PayOrderTarget.*(..))") |
|
<aop:after |
@After 等同于其他增强方式 |
Spring AOP项目应用——方法入参校验 & 日志横切的更多相关文章
- Spring Assert(方法入参检测工具类-断言)
Web 应用在接受表单提交的数据后都需要对其进行合法性检查,如果表单数据不合法,请求将被驳回.类似的,当我们在编写类的方法时,也常常需要对方法入参进行合 法性检查,如果入参不符合要求,方法将通过抛出异 ...
- 关于spring中Assert的应用(方法入参检测工具类)
关于spring中Assert的应用(方法入参检测工具类) Web 应用在接受表单提交的数据后都需要对其进行合法性检查,如果表单数据不合法,请求将被驳回.类似的,当我们在编写类的方法时,也常常需要对方 ...
- HandlerMethodArgumentResolver(一):Controller方法入参自动封装器【享学Spring MVC】
每篇一句 你的工作效率高,老板会认为你强度不够.你代码bug多,各种生产环境救火,老板会觉得你是团队的核心成员. 前言 在享受Spring MVC带给你便捷的时候,你是否曾经这样疑问过:Control ...
- js方法入参或局部变量和全局变量重名,用来赋值全局变量会失败
今天遇到个bug,最后终于知道原因了,js方法入参和全局变量重名,用入参赋值全局变量失败,就是说方法入参不能和全局变量重名. 现在下面的例子也说明,局部变量和全局变量不可以同名不光是入参,只要同名赋值 ...
- Mybatis方法入参处理
1,在单个入参的情况下,mybatis不做任何处理,#{参数名} 即可,甚至连参数名都可以不需要,因为只有一个参数,或者使用 Mybatis的内置参数 _parameter. 2,多个入参: 接口方法 ...
- frida框架hook参数获取方法入参模板
python脚本 # -*- coding: utf-8 -*- import logging import frida import sys logging.basicConfig(level=lo ...
- Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志
其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...
- 基于 Annotation 的 Spring AOP 权限验证方法的实现
1. 配置 applicationContext 在 Spring 中支持 AOP 的配置非常的简单,只需要在 Spring 配置文件 applicationContext.xml 中添加: < ...
- Spring aop+自定义注解统一记录用户行为日志
写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...
随机推荐
- cf343c 二分答案+模拟
/* 怎么判断能否在时间k内完成扫描 贪心:每次取出最靠左边的磁头去扫描最左边的,然后再往右扫描即可 如果当前点无法扫到最左侧点,那么后继点一样无法扫到 */ #include<bits/std ...
- RabbitMQ(一):RabbitMQ 安装与配置(Mac)
一.rabbitmq 安装与配置 安装: brew install rabbitmq # 进入安装目录 cd /usr/local/Cellar/rabbitmq/3.7.12 # 启动 brew s ...
- pika的阻塞式使用
[root@cloudplatform ELK]# cat startIncHouTai.py import os # 杀掉内存中的进程 cmd='pgrep -f PutDataToKafkaInc ...
- Oracle11g 创建数据库中问题处理(必须运行Netca以配置监听程序)
这两天学习<OCP/OCA认证考试指南>,要创建新的数据库,因为此前我的电脑上已经被折腾了好久的Mysql 和oracle10g ,所以可能导致很多环境都变了,创建数据库的过程中出现了一些 ...
- hdu 1588 求f(b) +f(k+b) +f(2k+b) +f((n-1)k +b) 之和 (矩阵快速幂)
g(i)=k*i+b; 0<=i<nf(0)=0f(1)=1f(n)=f(n-1)+f(n-2) (n>=2)求f(b) +f(k+b) +f(2*k+b) +f((n-1)*k + ...
- [转] equals和==的区别小结
==: == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象.比较的是真正意义上的指针操作. 1.比较的是操作符两端的操作数是否是同一个对象 ...
- python全栈开发day27-网络编程
回顾:1.两个架构:C/S B/S(统一了应用的接口)2.同一个电脑两个py文件通信(文件)3.两个电脑通信---网线4.交换机的通信方式:广播.单播.组播5.arp协议:通过ip地址找到对应的m ...
- Go 语言 IDE 之 VSCode 配置使用
Gogland 是 JetBrains 公司推出的 Go 语言集成开发环境.Gogland 同样基于 IntelliJ 平台开发,支持 JetBrains 的插件体系.官方:https://www.j ...
- BZOJ1195 [HNOI2006]最短母串 AC自动机 bfs
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 传送门 - BZOJ1195 题意概括 给出一堆串,然后求一个包含这些串的所有串的最短的中的字典序最小的. 题解 先造一个AC ...
- Spring框架中的Quartz定时任务使用笔记(通过@Scheduled注解的方式实现)
1.修改spring的xml配置信息 applicationContext.xml 三个部分内容 1.xmlns添加:xmlns:task="http://www.springframewo ...