SpringBoot AOP统一处理请求日志

SpringBoot AOP统一处理请求日志

有一天,项目经理老吴对小吴说:现在有这样一个需求:在SpringBootDemo01项目上的每个方法执行之前之后各打印一句话,例如:方法执行前打印如下内容:

方法执行开始

方法执行后打印:

方法执行完成

然后,小吴心里觉得,SpringBootDemo01项目中才不到10个方法,这个简单呀,好做嘛:直接在每个方法前后各添加一行打印输出代码就可以了。然后直接爽快的答应的,说5分钟给您。

5分钟过去了,老吴过来了,你高高心心的将程序跑起来,演示给老吴看:这是不是您想要的。

老吴说,很好,同样的需求,你把SpringBootDemo02中的每个方法执行前后各打印一句话。

由于有了第一个项目的基础,觉得这个用同样的方法一样的解决嘛,于是又爽快的答应了。说30分钟之后给您看,经理老吴走之后,打开SpringBootDemo02项目,发现里面居然有100个方法也,这该怎么办呢,然后小吴不带思考的重新开始了ctrl+c/ctrl+v。半个小时过去了,做的满头大汗,终于搞定了。于是又高高兴兴的演示个项目经理老吴看,你觉得这样Ok不?

项目经理老吴一看确实满足需求了,是的,就是这样,小吴呀,表现不错呦,工资加500。然后老吴就走了。10分钟之后,老吴回来了对你说:小吴呀,这个需求有一个小小的变动哈,方法执行前后打印的内容有变化如下:

方法执行start
方法执行finish

就这样一个小小的需求变动,你发现居然又要在每个方法中添加修改。
顿时小吴就傻眼了,难道真的要在每个方法中这样来修改将“开始”—>start,将“结束”—>finish。(这里只是为了说明这个道理,而没有考虑将打印的信息定义为常量哈,即使定义了常量,如果需求变为了在每个方法前后各打印一句:XXX方法start/finish)。

于是你就找到了技术搭档wojiushimogui,告诉了他你的困惑,wojiushimogui说呀:你傻呀,直接aop帮你做呀。那aop如何来做呢,具体如下:

第一步:在我们的SpringBoot项目中添加aop相关的依赖。

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

第二步:建立处理文件,并添加两个方法分别在每个方法执行之前和之后打印一句话。

    package com.wrh.aspect;

    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;

    /**
     * Created by wuranghao on 2017/4/14.
     */
    @Aspect
    @Component
    public class HttpAspect {

        /**
         * @Before 在方法执行之前执行
         * */
        @Before("execution(public * com.wrh.controller.StudentController.*(..))")
        public void log(){
            System.out.println("doBefore");

        }
        /**
         * @After在方法执行之后执行
         * */
        @After("execution(public * com.wrh.controller.StudentController.*(..))")
        public void doAfter(){
            System.out.println("doAfter");
        }
    }

既然使用aop就是避免重复代码来,而在注解@Before和@After中的内容完全一样,也可以提取出来,如何提取,如下:

    @Aspect
    @Component
    public class HttpAspect {
        @Pointcut("execution(public * com.wrh.controller.StudentController.*(..))")
        public void log(){}
        /**
         * @Before 在方法执行之前执行
         * */
        @Before("log()")
        public void doBefore(){
            System.out.println("doBefore");

        }
        /**
         * @After在方法执行之后执行
         * */
        @After("log()")
        public void doAfter(){
            System.out.println("doAfter");
        }
    }

这样,就是通过使用aop完成了在我们控制器StudentController所有方法执行前后各打印一句话的功能。

额外介绍另一个知识点:一般情况下我们不直接使用sout来进行输出,而是借助于org.slf4j.Logger类来进行相关日志的输出。相关使用代码如下:

    @Aspect
    @Component
    public class HttpAspect {

        private final  static org.slf4j.Logger logger= org.slf4j.LoggerFactory.getLogger(HttpAspect.class);

        @Pointcut("execution(public * com.wrh.controller.StudentController.*(..))")
        public void log(){}
        /**
         * @Before 在方法执行之前执行
         * */
        @Before("log()")
        public void doBefore(){
            logger.info("doBefore");
            //System.out.println("doBefore");

        }
        /**
         * @After在方法执行之后执行
         * */
        @After("log()")
        public void doAfter(){
            logger.info("doAfter");
            //System.out.println("doAfter");
        }
    }

日志输出结果如下,从结果中可以看出,其中包括来日志输出时间/类等相关信息,这明显比System.out.println输出相关信息要好得多。

回到今天来研究的需求:记录http请求,其中包括:http请求的url/请求的方法类型/响应该http请求的类方法/IP地址/请求中的参数

具体实现代码如下:

    /**
     * @Before 在方法执行之前执行
     * */
    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
        logger.info("doBefore");
        //记录http请求
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //从request中获取http请求的url/请求的方法类型/响应该http请求的类方法/IP地址/请求中的参数
        //url

        logger.info("url={}",request.getRequestURI());

        //method
        logger.info("method={}",request.getMethod());

        //ip
        logger.info("ip={}",request.getRemoteAddr());

        //类方法
        logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName()+
                "."+joinPoint.getSignature().getName());

        //参数

        logger.info("args={}",joinPoint.getArgs());

    }

可用如下的代码来获取响应的相关内容

    @AfterReturning(returning = "object",pointcut = "log()")
    public void doAfterReturning(Object object){
        logger.info("student={}",(Student)object);
    }

当借助于Postman工具输入如下的请求时,控制台的相关输出结果如下图所示。

参考资料

1、《慕课网-SpringBoot进阶之Web进阶》

2、http://blog.didispace.com/springbootaoplog/

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值