springboot+AOP切面监控接口调用详情

心之所向,素履以往。生如逆旅,一苇以航。

面试经常会被问到“你对spring是怎样理解的。”谈及spring,就不得不说它的两大核心spring的IOC(控制反转)和AOP(面向切面)。一直对于AOP的理解仅仅只是局限与它的基本概念,最近在实际项目中刚好用到,抽空整理下查找到的资料。

什么是AOP

AOP(Aspect-OrientedProgramming,面向切面编程),是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。使用AOP,可以将一些重复性的操作提取到主程序之外,这样可以降低模块之间的耦合度,使系统容易扩展同时也能使代码得到更好的复用。
AOP的主要应用场景为:日志记录,权限验证,接口调用情况监控,系统统一异常处理…

aop实践

本文主要使用aop面向切面的特点来实现监控接口的调用详情,以及系统统一的异常处理。

pom依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

具体实现

创建SectionMonitor类来进行接口调用监控

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;

/*
 *@name  springboot AOP切面监控接口调用详情
 *@author:  lyh_liu
 *@create   2020-05-19
 *
 **/
@Aspect
@Component
public class SectionMonitor {

    private static final ThreadLocal<Long> timeTreadLocal = new ThreadLocal<>();
    
    @Pointcut("execution(* com.lyh.aopdemo.controller..*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void log() {
    }
    @Before("log()")
    public void before(JoinPoint joinPoint) {
        timeTreadLocal.set(System.currentTimeMillis());
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取请求的request
        HttpServletRequest request = attributes.getRequest();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取被拦截的方法
        Method method = methodSignature.getMethod();
        //获取被拦截的方法名
        String methodName = method.getName();
        System.out.println("接口方法名称:" + methodName + "()");
        //获取所有请求参数key和value
        String keyValue = getReqParameter(request);
        System.out.println("url = "+ request.getRequestURL().toString());
        System.out.println("方法类型 = "+ request.getMethod());
        System.out.println("请求参数 key:value = "+ keyValue);
    }


    @After("log()")
    public void after() {
        System.out.println("aop的after()方法");
    }

    //controller请求结束返回时调用
    @AfterReturning(returning = "result", pointcut = "log()")
    public Object afterReturn(Object result) {
        System.out.println("返回值result ="+ result.toString());
        long startTime = timeTreadLocal.get();
        double callTime = (System.currentTimeMillis() - startTime) / 1000.0;
        System.out.println("调用接口共花费时间time = "+ callTime+" s");
        return result;
    }

    /**
     * 获取所有请求参数,封装为map对象
     *
     * @return
     */
    public Map<String, Object> getParameterMap(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String, Object> parameterMap = new HashMap<String, Object>();
        StringBuilder stringBuilder = new StringBuilder();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getParameter(key);
            String keyValue = key + " : " + value + " ; ";
            stringBuilder.append(keyValue);
            parameterMap.put(key, value);
        }
        return parameterMap;
    }

    public String getReqParameter(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        Enumeration<String> enumeration = request.getParameterNames();
        //StringBuilder stringBuilder = new StringBuilder();
        JSONArray jsonArray = new JSONArray();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getParameter(key);
            JSONObject json = new JSONObject();
            json.put(key, value);
            jsonArray.add(json);
        }
        return jsonArray.toString();
    }
}

@Pointcut("execution(* com.lyh.aopdemo.controller….(…))是用来设置具体的切面位置。具体的介绍可参考:Pointcut表达式介绍

controller层

@Controller
@RequestMapping("/text")
public class TestController {
    @RequestMapping(value="/aspectJMethod",method = RequestMethod.POST)
    public String   aspectJMethod(){
        return "hello AOP";
    }
}

注:在接口使用注解@PostMapping或@GetMapping时aop时会出现切面不起作用需使用@RequestMapping。

2020 在学习的道路勇往直前。


版权声明:本文为LYH_Loner原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。