【Java】AOP面向切面写一个日志拦截器

在这里插入图片描述
封面作者:yemoze1314

一、问题

1.1、环境
电脑环境:Windows 10;
开发工具:IntelliJ IDEA;
数据库环境:Redis 3.2.100
JDK环境: Jdk1.8;

1.2、问题
因为我想看每个请求过来的入参和出参,所以结合 SpringBoot 的AOP(面向切面编程)写了一个日志打印的方法;那么具体是如何实现的呢?

二、解答

1.jar包依赖,下面这3个依赖包是切面功能实现的必要条件;

        <!-- 面向切面  added by Taozhouchuan at 2020-8-3 19:26:53   :http://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <!-- http://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <!-- http://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.2</version>
        </dependency>

2.AOP的point Cut切面的切点

private final String POINT_CUT = "execution(public * com.demo.controller.*.*(..))";

3.Spring 的AOP切点主要有5种:

package com.demo.filter;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
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.util.Arrays;

/**
 * 拦截器
 * @author tzc
 */
@Aspect
@Component
@Slf4j
public class LogIntercept {

    ThreadLocal<Long> startTime = new ThreadLocal<Long>();

    private final String POINT_CUT = "execution(public * com.demo.controller.*.*(..))";

    /**
     * 切点表达式中,..两个点表明多个,*代表一个,  上面表达式代表切入com.demo.controller包下的所有类的所有方法,
     * 方法参数不限,返回类型不限。  其中访问修饰符可以不写,不能用*,,第一个*代表返回类型不限,第二个*表示所有类,第三个*表示所有方法,
     * ..两个点表示方法里的参数不限。  然后用@Pointcut切点注解,想在一个空方法上面,
     */
    /**
     * 指定切面,这里可以是一个空方法;
    */
    @Pointcut(POINT_CUT)
    private void pointCut() {
    }



    //请求method前打印内容
    @Before(value = "pointCut()")
    public void before(JoinPoint joinPoint) {
        startTime.set(System.currentTimeMillis());

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //打印请求内容
        log.error("=================[Before]请求内容===============");
        log.info("==================[Before]请求地址:{}", request.getRequestURL().toString());
        log.info("==================[Before]请求方式:{}", request.getMethod());
        log.info("==================[Before]请求类方法:{}", joinPoint.getSignature());
        log.info("==================[Before]请求类方法参数:{}", Arrays.toString(joinPoint.getArgs()));
        log.info("==================[Before]请求内容===============");
    }

   
    //环绕执行:定义需要匹配的切点表达式,同时需要匹配参数
    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("==================[Around]方法环绕前置start=====================>");
        //这句必须有 往下执行方法
        Object result = pjp.proceed();

        log.info("==================[Around]方法环绕后置start=====================>");
        log.info("==================[Around]耗时(毫秒):{}", (System.currentTimeMillis() - startTime.get()));
        log.info("==================[Around]返回数据:{}", result);
        log.info("==================[Around]方法环绕end======================>");
        return result;
    }

	//方法执行后
    @After(value = "pointCut()")
    public void after(){
        log.debug("==================[After]切点方法执行后");
    }

	//方法异常拦截
    @AfterThrowing(value = "pointCut()")
    public void afterThrowing(){
        log.debug("==================[AfterThrowing]切点方法抛出异常");
    }


    //在方法执行完结后打印返回内容
    @AfterReturning(returning = "o", pointcut = "pointCut()")
    public void afterReturning(Object o) {
        log.info("==================[AfterReturning]Response内容:" + JSON.toJSONString(o));
        log.info("==================[AfterReturning]请求消耗时间:{}ms", (System.currentTimeMillis() - startTime.get()));

    }

}

这样,一个从0到功能实现的日志打印的拦截器就完成了;控制台打印日志如下:
在这里插入图片描述

完毕~

三、总结

如果你照着上面的方法配置,如果还是没有被拦截(因为没有拦截不会报错),所以请着重从上面3个角度排查:

1、jar包是否在pom.xml文件中正确引用,或者library包下载正确,并关联到项目;
2、pointCut 的execute 的拦截的路径是否正确,这里着重查看;
3、java代码有没有bug;

欢迎关注我的
CSDN博客: https://blog.csdn.net/River_Continent
微信公众号:幕桥社区
在这里插入图片描述
知乎:张牧野, https://www.zhihu.com/people/zhang-mu-ye-37-76/activities
简书: https://www.jianshu.com/u/02c0096cbfd3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陶洲川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值