转载:https://blog.csdn.net/Daybreak1209/article/details/80591566

应用一:方法入参校验

由于系统多个方法入参均对外封装了统一的Dto,其中Dto中几个必传参数在每个方法中都会进行相同的校验逻辑。笔者考虑采用Spring AOP进行优化,拦截方法进行参数校验。测试case实现如下:

Before

  1.  
    /**
  2.  
    * 被代理的目标类
  3.  
    */
  4.  
    @Service
  5.  
    public class PayOrderTarget {
  6.  
    @Autowired
  7.  
    private PaymentOrderService paymentOrderService;
  8.  
     
  9.  
    public Result testQuery(QueryPaymentDto paymentOrderDto){
  10.  
    PaymentOrderDto paymentOrderDto1 = paymentOrderService.queryPaymentOrder(paymentOrderDto);
  11.  
    return ResultWrapper.success(paymentOrderDto1);
  12.  
    }
  13.  
    }
  1.  
    /**
  2.  
    * 通知类,横切逻辑
  3.  
    */
  4.  
    @Component
  5.  
    @Aspect
  6.  
    public class Advices {
  7.  
     
  8.  
    @Before("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")
  9.  
    public Result before(JoinPoint proceedingJoinPoint) {
  10.  
    // 拦截获取入参
  11.  
    Object[] args = proceedingJoinPoint.getArgs();
  12.  
    // 入参校验
  13.  
    if (args ==null){
  14.  
    return ResultWrapper.fail();
  15.  
    }
  16.  
     
  17.  
    String input = JSON.toJSON(args).toString();
  18.  
    Map<String, String> map = Splitter.on(",").withKeyValueSeparator(":").split(input);
  19.  
    if (map.containsKey("businessId") || map.containsKey("payOrderId")){
  20.  
    System.out.println("key null");
  21.  
    return ResultWrapper.fail();
  22.  
    }
  23.  
    if (map.get("businessId")==null || map.get("payOrderId")==null ){
  24.  
    System.out.println("value null");
  25.  
    return ResultWrapper.fail();
  26.  
    }
  27.  
     
  28.  
    System.out.println("----------前置通知----------");
  29.  
    System.out.println(proceedingJoinPoint.getSignature().getName());
  30.  
    return ResultWrapper.success();
  31.  
    }
  32.  
    }

测试类正常调用查询方法

  1.  
    @RunWith(SpringJUnit4ClassRunner.class)
  2.  
    @ContextConfiguration(locations = {"classpath*:spring/spring-context.xml"})
  3.  
    public class Test {
  4.  
     
  5.  
    @Autowired
  6.  
    private PayOrderTarget payOrderTarget;
  7.  
     
  8.  
    @org.junit.Test
  9.  
    public void test(){
  10.  
    QueryPaymentDto paymentOrderDto=new QueryPaymentDto();
  11.  
    paymentOrderDto.setPayOrderId(11l);
  12.  
    paymentOrderDto.setBusinessId(112L);
  13.  
    paymentOrderDto.setPageSize(1);
  14.  
    payOrderTarget.testQuery(paymentOrderDto);
  15.  
    }
  16.  
    }

执行结果可对入参Dto进行拦截,执行before中的校验逻辑。但即便return fail之后,目标方法还是会被执行到。笔者是想实现参数校验失败,则直接返回,不执行接下来查询db的操作。此时则需要使用Around切入。

Around

  1.  
    @Around("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")
  2.  
    public Result around(ProceedingJoinPoint proceedingJoinPoint) {
  3.  
    // 获取java数组
  4.  
    Object[] args = proceedingJoinPoint.getArgs();
  5.  
    JSONArray jsonArray = JSONArray.parseArray(JSONArray.toJSONString(args));
  6.  
    String businessId = null;
  7.  
    String payOrderId = null;
  8.  
    for (int i = 0; i < jsonArray.size(); i++) {
  9.  
    businessId = jsonArray.getJSONObject(0).getString("businessId");
  10.  
    payOrderId = jsonArray.getJSONObject(0).getString("payOrderId");
  11.  
    }
  12.  
    ;
  13.  
    //参数校验
  14.  
    if (businessId == null || payOrderId == null) {
  15.  
    System.out.println("value null");
  16.  
    return ResultWrapper.fail();
  17.  
    }
  18.  
            //执行目标方法
  19.  
    try {
  20.  
    proceedingJoinPoint.proceed();
  21.  
    } catch (Throwable throwable) {
  22.  
    throwable.printStackTrace();
  23.  
    }
  24.  
    System.out.println("----------Around通知----------");
  25.  
    return ResultWrapper.success();
  26.  
    }

简单介绍下,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 在方法抛出异常后调用通知
  1.  
    /**
  2.  
    * login接口类 被代理接口类
  3.  
    */
  4.  
    public interface ILoginService {
  5.  
    boolean login(String userName, String password);
  6.  
    void quary() throws Exception;
  7.  
    }
  8.  
     
  9.  
    /**
  10.  
    * login实现类,被代理目标类
  11.  
    */
  12.  
    @Service
  13.  
    public class LoginServiceImpl implements ILoginService {
  14.  
     
  15.  
    public boolean login(String userName, String password) {
  16.  
    System.out.println("login:" + userName + "," + password);
  17.  
    return true;
  18.  
    }
  19.  
    @Override
  20.  
    public void quary() throws Exception{
  21.  
    System.out.println("******quary*******");
  22.  
    // 测试方法抛出异常后,解注After-throwing配置,会执行logArg切入,不抛异常不执行切点
  23.  
    // int i=10/0;
  24.  
    }
  25.  
    }

日志包装类,将各类日志情况进行方法封装

  1.  
    /**
  2.  
    * 日志处理类
  3.  
    */
  4.  
    public interface ILogService {
  5.  
    //无参的日志方法
  6.  
    void log();
  7.  
     
  8.  
    //有参的日志方法
  9.  
    void logArg(JoinPoint point);
  10.  
     
  11.  
    //有参有返回值的方法
  12.  
    void logArgAndReturn(JoinPoint point, Object returnObj);
  13.  
    }
  14.  
     
  15.  
    /**
  16.  
    * 日志实现类
  17.  
    */
  18.  
    @Component("logService")
  19.  
    @Aspect
  20.  
    public class LogServiceImpl implements ILogService {
  21.  
     
  22.  
    @Override
  23.  
    public void log() {
  24.  
    System.out.println("*************Log*******************");
  25.  
    }
  26.  
    @Override
  27.  
    public void logArg(JoinPoint point) {
  28.  
    System.out.println("方法:"+point.getSignature().getName());
  29.  
    Object[] args = point.getArgs();
  30.  
    System.out.println("目标参数列表:");
  31.  
    if (args != null) {
  32.  
    for (Object obj : args) {
  33.  
    System.out.println(obj + ",");
  34.  
    }
  35.  
    }
  36.  
    }
  37.  
    @Override
  38.  
    public void logArgAndReturn(JoinPoint point, Object returnObj) {
  39.  
    //此方法返回的是一个数组,数组中包括request以及ActionCofig等类对象
  40.  
    Object[] args = point.getArgs();
  41.  
    System.out.println("目标参数列表:");
  42.  
    if (args != null) {
  43.  
    for (Object obj : args) {
  44.  
    System.out.println(obj + ",");
  45.  
    }
  46.  
    System.out.println("执行结果是:" + returnObj);
  47.  
    }
  48.  
    }
  49.  
    }

配置(注释着重看下):

  1.  
    <aop:config>
  2.  
    <!-- 切入点 LoginServiceImpl类中所有方法都会被拦截,执行增强方法-->
  3.  
    <aop:pointcut expression="execution(* com.payment.util.aoplogger.LoginServiceImpl.*(..))" id="myPointcut" />
  4.  
    <!-- 切面-->
  5.  
    <aop:aspect id="dd" ref="logService">
  6.  
    <!-- LoginServiceImpl类方法执行前,增强执行日志service的log方法-->
  7.  
    <!--<aop:before method="log" pointcut-ref="myPointcut" />-->
  8.  
     
  9.  
    <!-- LoginServiceImpl类方法执行后,增强执行日志service的logArg方法-->
  10.  
    <!--<aop:after method="logArg" pointcut-ref="myPointcut"/>-->
  11.  
    <!-- LoginServiceImpl类方法执行抛异常后,增强执行日志service的logArg方法-->
  12.  
    <aop:after-throwing method="logArg" pointcut-ref="myPointcut"/>
  13.  
    <!--<aop:after-returning method="logArgAndReturn" returning="returnObj" pointcut-ref="myPointcut"/>-->
  14.  
    </aop:aspect>
  15.  
    </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项目应用——方法入参校验 & 日志横切的更多相关文章

  1. Spring Assert(方法入参检测工具类-断言)

    Web 应用在接受表单提交的数据后都需要对其进行合法性检查,如果表单数据不合法,请求将被驳回.类似的,当我们在编写类的方法时,也常常需要对方法入参进行合 法性检查,如果入参不符合要求,方法将通过抛出异 ...

  2. 关于spring中Assert的应用(方法入参检测工具类)

    关于spring中Assert的应用(方法入参检测工具类) Web 应用在接受表单提交的数据后都需要对其进行合法性检查,如果表单数据不合法,请求将被驳回.类似的,当我们在编写类的方法时,也常常需要对方 ...

  3. HandlerMethodArgumentResolver(一):Controller方法入参自动封装器【享学Spring MVC】

    每篇一句 你的工作效率高,老板会认为你强度不够.你代码bug多,各种生产环境救火,老板会觉得你是团队的核心成员. 前言 在享受Spring MVC带给你便捷的时候,你是否曾经这样疑问过:Control ...

  4. js方法入参或局部变量和全局变量重名,用来赋值全局变量会失败

    今天遇到个bug,最后终于知道原因了,js方法入参和全局变量重名,用入参赋值全局变量失败,就是说方法入参不能和全局变量重名. 现在下面的例子也说明,局部变量和全局变量不可以同名不光是入参,只要同名赋值 ...

  5. Mybatis方法入参处理

    1,在单个入参的情况下,mybatis不做任何处理,#{参数名} 即可,甚至连参数名都可以不需要,因为只有一个参数,或者使用 Mybatis的内置参数 _parameter. 2,多个入参: 接口方法 ...

  6. frida框架hook参数获取方法入参模板

    python脚本 # -*- coding: utf-8 -*- import logging import frida import sys logging.basicConfig(level=lo ...

  7. Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...

  8. 基于 Annotation 的 Spring AOP 权限验证方法的实现

    1. 配置 applicationContext 在 Spring 中支持 AOP 的配置非常的简单,只需要在 Spring 配置文件 applicationContext.xml 中添加: < ...

  9. Spring aop+自定义注解统一记录用户行为日志

    写在前面 本文不涉及过多的Spring aop基本概念以及基本用法介绍,以实际场景使用为主. 场景 我们通常有这样一个需求:打印后台接口请求的具体参数,打印接口请求的最终响应结果,以及记录哪个用户在什 ...

随机推荐

  1. PyCharm Professional破解版和汉化下载地址-new

    2018.1版本下载地址 2018.2.1版本下载地址 今天找了很久很多都不能用了,注意破解过程提到的小细节,如果破解完了点击没反应请检查"pycharm.exe.vmoptions&quo ...

  2. 《转》String,StringBuffer与StringBuilder的区别??

    String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简要的说, String 类型和 StringBuffer 类型的主要性能 ...

  3. POJ2115 C Looooops 扩展欧几里德

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ2115 题意 对于C的for(i=A ; i!=B ;i +=C)循环语句,问在k位存储系统中循环几次 ...

  4. 037 对于HIVE架构的理解

    0.发展 在hive公布源代码之后 公司又公布了presto,这个比较快,是基于内存的. impala:3s处理1PB数据. 1.Hive  能做什么,与 MapReduce 相比优势在哪里 关于hi ...

  5. 001.HAProxy简介

    一 HAProxy简介 HAProxy是可提供高可用性.负载均衡以及基于TCP(从而可以反向代理mysql等应用)和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProx ...

  6. 只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时

    HttpContext context = HttpContext.Current; //而后,再使用: if (context.Session["user"] != null) ...

  7. 浅谈CSS和JQuery实现鼠标悬浮图片放大效果

    对于刚刚学习网页前台设计的同学一定对图片的处理非常苦恼,那么这里简单的讲解一下几个图片处理的实例. 以.net为平台,微软的Visual Studio 2013为开发工具,当然前台技术还是采用CSS3 ...

  8. 项目冲刺Third

    Third Sprint PS:经今天讨论,我们小队决定撤掉用GUI设计界面,改用html和php制作,所以需要修改前面的博客及重新分配任务 1.各个成员今日完成的任务 蔡振翼:撰写博客 谢孟轩:借阅 ...

  9. loj#2574. 「TJOI2018」智力竞赛 (路径覆盖)

    目录 题目链接 题解 代码 题目链接 loj#2574. 「TJOI2018」智力竞赛 题解 就是求可重路径覆盖之后最大化剩余点的最小权值 二分答案后就是一个可重复路径覆盖 处理出可达点做二分图匹配就 ...

  10. TYVJ.1864.[Poetize I]守卫者的挑战(概率DP)

    题目链接...无 题目: P1864 [Poetize I]守卫者的挑战 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 打开了黑魔法师Vani的大门,队员们 ...