Java, 原创, 服务器, , ,

记录Spring boot 项目 AOP Aspect 实现环绕通知,含参数获取,结果处理(统一返回参数),异常抛出

业务需要,也不想每次返回的结果都手动整理一次数据,另外 com.alibaba.cola.dto.MultiResponse 的字段不够我们需显示的信息,先说下实际需求

1.针对单条记录或者单个字段返回,我们希望返回格式统一为

{
  "data": {

    "id": 41,
    "status": 1
  },
  "errCode": 200,
  "success": true,
  "requestId": "f79f656-bf23-712a-ce55-45a51ba1fa72c",
  "errMessage": ""
}
或
{
  "data": 20,
  "errCode": 200,
  "success": true,
  "requestId": "f79f656-bf23-712a-ce55-45a51ba1fa72c",
  "errMessage": ""
}

对多行数据返回,我们希望 格式如下

{
  "total": 4,
  "data": [
    {
      "id": 41,
      "status": 1
    }
  ],
  "size": 1,
  "errCode": 200,
  "success": true,
  "totalPage": 4,
  "requestId": "5e02379-37f4-e678-b716-32a7932d9e7e8",
  "page": 1,
  "errMessage": ""
}

AOP 获取请求参数中的page, size, 这样就可以在结果中统一添加分页信息, 需要在前置通知或环绕通知中获得,代码如下:

        int page = 0;
 
        int size = 0;
        //前置通知
        //RequestBody中参数使用下面获取方式
        Object[] args = proceedingJoinPoint.getArgs();
        for ( args != null && Object arg : args) {
            System.out.println(arg.toString());
            JSONObject jsonObject = new JSONObject(arg);

            System.out.println(jsonObject.toString());
            if ( jsonObject != null && jsonObject.has("page") ) {
                int pageParam = jsonObject.getInt("page");
                if (pageParam > 0) {
                    page = pageParam;
                }
            }
            if ( jsonObject != null && j jsonObject.has("page") ) {
                int sizeParam = jsonObject.getInt("size");
                if (sizeParam > 0) {
                    size = sizeParam;
                }
            }
        }

如果需要获取头部信息,可以这样获取

ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
//获取头部信息
String userAgent = request.getHeader("User-Agent");
System.out.println(userAgent);

如何处理返回的结果,

            //方法执行
            System.out.println("方法执行");
            //proceed 就是 controller 中获取到的返回值 (返回值拼错了 (lll¬ω¬))
            processd = proceedingJoinPoint.proceed();

            System.out.println("后置通知");
            if (processd == null) {
                System.out.println("无返回值");
                return null;
            }
            //后置通知
            if (processd instanceof MultiResponse) {
                MultiResponse multiResponse = (MultiResponse) processd;
                String errCode = multiResponse.getErrCode();
                map.put("errCode", errCode == null ? 200 : errCode);
                String errMessage =  multiResponse.getErrMessage();
                map.put("errMessage", errMessage == null ? "" : errMessage);
                map.put("success", multiResponse.isSuccess());
                map.put("data", multiResponse.getData());
                int total =  multiResponse.getTotal();
                map.put("total", total);
                if (size > 0) {
                    map.put("page", page);
                    map.put("size", size);
                    double totalPage = Math.ceil((double) total / size);
                    //System.out.println(total + " / " + size + " totalPage: " + totalPage);
                    map.put("totalPage",  (int) totalPage);
                }
                System.out.println("多行 " + map.toString());
            } else if (processd instanceof Response) {
                Response response = (Response) processd;
                String errCode = response.getErrCode();
                map.put("errCode", errCode == null ? 200 : errCode);
                //map.put("errCode", response.getErrCode());
                String errMessage =  response.getErrMessage();
                map.put("errMessage", errMessage == null ? "" : errMessage);
                //map.put("errMessage", response.getErrMessage());
                map.put("success", response.isSuccess());
                System.out.println("单行Response " + map.toString());
            } else {
                map.put("errCode", 200);
                map.put("errMessage", "");
                map.put("data", processd);
                map.put("success", true);
                System.out.println("其他对象 类型" + map.toString());
            }
            String requestId = buildRequestId();
            map.put("requestId", requestId);
            //System.out.println(map.toString());
            return map;

Controller和 service 应该如何定义返回值

//conroller 中返回值 全部定义为 Object 无论service返回什么类型数据
    @PostMapping(value = "/index")
    @ResponseBody
    public Object findAllSupplier(@RequestBody NameSupplierCO nameSupplierCO) {
        //获取到的结果是 MuliResponse<SupplierCO>
        Object object = cdSupplierService.findAll(nameSupplierCO);
        return object;
    }
    @PostMapping(value = "/create")
    @ResponseBody
    public Object addSupplier(@Validated @RequestBody AddSupplierCO addSupplierCO) {
        //获取到的结果为  int ,添加记录的自增ID值
        return cdSupplierService.addSupplier(addSupplierCO);
    }

Service定义的接口

    //添加供应商
    public int addSupplier(AddSupplierCO addSupplierCO);
    //获取列表
    public MultiResponse<SupplierCO> findAll(NameSupplierCO nameSupplierCO);

整体AOP代码如下

package com.example.test.aop;

import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.Response;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;


import com.alibaba.cola.logger.Logger;
import com.alibaba.cola.logger.LoggerFactory;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * Create By cx<chenxue4076@163.com>
 * File Name MultiResponseAspect.java
 * Created Date 2020/11/24
 * Created Time 15:52
 */
@Component
@Aspect
public class MultiResponseAspect {
    private static Logger logger = LoggerFactory.getLogger(MultiResponseAspect.class);
    //后置通知, 最终结果处理
    /*@AfterReturning(pointcut = "execution(* cn.focusmedia.fcs.controller..*.*(..))", returning = "returningValue")
*/

    //获取需嵌套的切面,我们劫持所有controller, 如果需要去除指定使用 and !execuion(xxxx)
    @Pointcut("execution(* cn.focusmedia.fcs.controller..*.*(..))")
    public void aroundResponse() {}

    //环绕通知
    @Around("aroundResponse()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) {
        //如果有分页,我们需要给结果添加 page, size, totalPage 等参数
        int page = 0;
 
        int size = 0;
        //前置通知
        System.out.println("前置通知在这里显示");
        //ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //HttpServletRequest request = servletRequestAttributes.getRequest();
        //获取头部信息
        //String userAgent = request.getHeader("User-Agent");
        //System.out.println(userAgent);
        //RequestBody中参数使用下面获取方式
        Object[] args = proceedingJoinPoint.getArgs();
        for ( args != null && Object arg : args) {
            System.out.println(arg.toString());
            JSONObject jsonObject = new JSONObject(arg);

            System.out.println(jsonObject.toString());
            if ( jsonObject != null && jsonObject.has("page") ) {
                int pageParam = jsonObject.getInt("page");
                if (pageParam > 0) {
                    page = pageParam;
                }
            }
            if ( jsonObject != null && j jsonObject.has("page") ) {
                int sizeParam = jsonObject.getInt("size");
                if (sizeParam > 0) {
                    size = sizeParam;
                }
            }
        }
        //定义返回值类型
        Object processd = null;
        Map<String, Object> map = new HashMap<String, Object>();
        try{
            //方法执行
            System.out.println("方法执行");
            processd = proceedingJoinPoint.proceed();

            System.out.println("后置通知");
            if (processd == null) {
                System.out.println("无返回值");
                return null;
            }
            //后置通知
            if (processd instanceof MultiResponse) {
                MultiResponse multiResponse = (MultiResponse) processd;
                String errCode = multiResponse.getErrCode();
                map.put("errCode", errCode == null ? 200 : errCode);
                String errMessage =  multiResponse.getErrMessage();
                map.put("errMessage", errMessage == null ? "" : errMessage);
                map.put("success", multiResponse.isSuccess());
                map.put("data", multiResponse.getData());
                int total =  multiResponse.getTotal();
                map.put("total", total);
                if (size > 0) {
                    map.put("page", page);
                    map.put("size", size);
                    double totalPage = Math.ceil((double) total / size);
                    //System.out.println(total + " / " + size + " totalPage: " + totalPage);
                    map.put("totalPage",  (int) totalPage);
                }
                System.out.println("多行 " + map.toString());
            } else if (processd instanceof Response) {
                Response response = (Response) processd;
                String errCode = response.getErrCode();
                map.put("errCode", errCode == null ? 200 : errCode);
                //map.put("errCode", response.getErrCode());
                String errMessage =  response.getErrMessage();
                map.put("errMessage", errMessage == null ? "" : errMessage);
                //map.put("errMessage", response.getErrMessage());
                map.put("success", response.isSuccess());
                System.out.println("单行Response " + map.toString());
            } else {
                map.put("errCode", 200);
                map.put("errMessage", "");
                map.put("data", processd);
                map.put("success", true);
                System.out.println("其他对象 类型" + map.toString());
            }
            String requestId = buildRequestId();
            map.put("requestId", requestId);
            //System.out.println(map.toString());
            return map;

        } catch (Throwable e) {
            //异常通知
            logger.error("异常抛出" + e.getMessage());
            System.out.println("异常抛出" + e.getMessage());
            System.out.println("异常抛出");
            Response response = new Response();
            response.setErrCode("500");
            response.setErrMessage(e.getMessage().substring(0));
            response.setSuccess(false);
            processd = response;
        } finally {
            //最终通知
            System.out.println("最终通知");

            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            Class<?> returnType = methodSignature.getReturnType();
            String className = returnType.getName();

            if( className.contains("Object") ) {
                System.out.println("最终通知 Object");
                if (map.isEmpty() == false) {
                    return (Object) map;
                }
            } else if( className.contains("Response") || className.contains("MultiResponse")) {
                System.out.println("最终通知 Response");
                if (processd != null) {
                    return processd;
                }
            }
            if (processd != null) {
                System.out.println("最终通知未知");
                return processd;
            }
            return null;
        }
    }

    public String buildRequestId() throws Exception{
        byte[] sourceString = String.valueOf(System.currentTimeMillis()).getBytes();
        String md5Code = DigestUtils.md5DigestAsHex(sourceString);
        return md5Code.substring(0, 7) + "-" + md5Code.substring(7, 11) + "-"+ md5Code.substring(11,15)+"-"+md5Code.substring(15,19)+"-"+md5Code.substring(19);
    }
}

// end MultiResponseAspect.java
//end file

通过添加此 AOP处理, 我们的结果就整体统一了, 而不是我们创建一个自定义返回值的类,每个结果都被处理成该类, 这样不仅麻烦,而且还不容易维护。 而AOP很好的实现了统一。

另一篇 AOP切面Mapper实现读写分离的介绍

(91)

Author Since: Jul 05, 2018

Related Post