zoukankan      html  css  js  c++  java
  • spring boot 中AOP的使用

    一、AOP统一处理请求日志

    也谈AOP

    1、AOP是一种编程范式

    2、与语言无关,是一种程序设计思想

    • 面向切面(AOP)Aspect Oriented Programming
    • 面向对象(OOP)Object Oriented Programming
    • 面向过程(POP) Procedure Oriented Programming

     

    再谈AOP

    1、面向过程到面向对象

    2、换个角度看世界,换个姿势处理问题 

    3、将通用逻辑从业务逻辑中分离出来

     

    二、处理过程

    个人理解,其实就是日志体系为了方便使用,可以用log4j的思维去理解下。

     

    三、Aop的实际应用

    1、准备工作

    在pom中添加aop依赖,具体示例如下:

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

    2、日志输出

    比如我们在实际项目中,期望我们操作的每一步都有日志输出,那么我们该怎么做呢,还是用前面学生信息的源代码,来进行演示。

    首先创建一个切面,这里和spring中的aop其实都一样的,可以说是更简便了,具体示例代码如下:

    package com.rongrong.springboot.demo.aspect;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    /**
     * @author rongrong
     * @version 1.0
     * @description:
     * @date 2020/1/6 21:50
     */
    @Aspect
    @Component
    public class HttpAspect {
    
        @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
            System.out.println("我执行了!!");
        }
    }

    接着启动项目,调用下查询接口,控制台输出如下图打印内容,证明成功

    3、日志输出代码优化

    有before,肯定就会有after,即调用时有日志输出,调用完也有结果输出,一来方便自己调试,二来也方便查看报错,那么after怎么写呢?我猜一般同学肯定都这么干。

    package com.rongrong.springboot.demo.aspect;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    /**
     * @author rongrong
     * @version 1.0
     * @description:
     * @date 2020/1/6 21:50
     */
    @Aspect
    @Component
    public class HttpAspect {
    
        @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
            System.out.println("我执行了!!");
        }
    
        @After("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void afterlog(){
            System.out.println("我执行了!!");
        }
    }

    这样写一点毛病也没有,但是。。。。。。。。。。。。。。。。。。。。哇哈哈哈哈哈哈,你肯定会说,我肯定不这样写,可以不承认,但有些同学肯定是这样干的。

    写代码的原则,尽量少写重复代码,为啥呢?维护成本高呀,再就是让人觉得你的代码很low逼,看到这你肯定不会那么干了吧,哈哈哈哈哈。

    好了玩笑开完了,我们可以这样,声明个切点,再切点里维护想要的切面,具体代码示例如下:

    package com.rongrong.springboot.demo.aspect;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    /**
     * @author rongrong
     * @version 1.0
     * @description:
     * @date 2020/1/6 21:50
     */
    @Aspect
    @Component
    public class HttpAspect {
    
        @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
        }
        
        @Before("log()")
        public void doBefore(){
            System.out.println("我执行了!!");
        }
        
        @After("log()")
        public void doAfter(){
            System.out.println("我执行了!!");
        }
    }

    4、Self4j的使用

    改了下,发现似乎还是有点low,都用spring boot框架了,咋还能用System.out.println()输出呢,那么怎么优化呢?

    现在仅仅满足了,控制台输出内容,但是如果我想要的日志不是这样的,最基本的得上面一样吧,有日期、端口、方法名之类的,即项目启动时控制台这样的日志才好看些吧,使用spring boot框架自带日志self4j即可解决,具体代码示例如下:

    package com.rongrong.springboot.demo.aspect;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    /**
     * @author rongrong
     * @version 1.0
     * @description:
     * @date 2020/1/6 21:50
     */
    @Aspect
    @Component
    public class HttpAspect {
    
       /**
         * 使用self4j,此日志为spring boot自带的日志框架
         */
        private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class);
    
        @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
        }
        
        @Before("log()")
        public void doBefore(){
            logger.info("我执行了!!");
        }
        
        @After("log()")
        public void doAfter(){
            logger.info("我执行了!!");
        }
    }

    启动项目后,如下图所示,证明我们成功了

    5、请求参数及响应信息控制台输出

    这似乎看起来好了很多,但是实际工作时候,为了方便调试需要把我们请求接口及请求后返回的响应信息,在控制台输出,方便我们调试定位问题,下面我们来进行演示如何从控制台输出这些信息。

    5.1、输出请求参数信息

    使用RequestContextHolder来获得请求参数相关属性,这里需要强转成ServletRequestAttributes对象,Joinpoint这个参数非必须,是在获取“类方法”、“类名”、“方法参数”的时候会用到,如果用不到的话就不需要了。

    具体示例代码如下所示:

    package com.rongrong.springboot.demo.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    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.jws.Oneway;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @author rongrong
     * @version 1.0
     * @description:
     * @date 2020/1/6 21:50
     */
    @Aspect
    @Component
    public class HttpAspect {
    
        /**
         * 使用self4j,此日志为spring boot自带的日志框架
         */
        private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class);
    
        /**
         *此处为了简化代码,提高维护性,还是需要提炼下的
        @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
            System.out.println("我执行了!!");
        }
         */
    
        @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
        }
    
        /**
         * 在接口执行操作时输出相关参数
         */
        @Before("log()")
        public void doBefore(JoinPoint joinPoint){
            //使用RequestContextHolder来获得请求属性,这里需要强转成ServletRequestAttributes对象
            ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            //获取请求url
            String url = servletRequestAttributes.getRequest().getRequestURI();
            //获取请求IP
            String addr = servletRequestAttributes.getRequest().getRemoteAddr();
            //获取请求方法
            String method = servletRequestAttributes.getRequest().getMethod();
            //获取类名
            String pCName = joinPoint.getSignature().getDeclaringTypeName();
            //获取类方法
            String cName = joinPoint.getSignature().getName();
            //这里要说明下 logger.info("url={}",url),url为{}自动填充部分
            //url
            logger.info("url= {}",url);
            //ip
            logger.info("ip= {}",addr);
            //method
            logger.info("method= {}",method);
            //args
            //获取请求参数
            logger.info("args= {}",joinPoint.getArgs());
            //类名和类方法
            logger.info("类名和类方法= {}",pCName+"."+cName);
        }
    
        @After("log()")
        public void doAfter(){
            logger.info("doAfter :我执行了!!");
        }
    
    }

    重新启动项目,我们调用下查询所有学生接口,控制台显示如下信息,证明日志成功!

    5.2、输出响应信息

    接下来我们再来输出响应结果信息,使用注解@AfterReturning,获取返回相应信息,具体示例代码如下:

    package com.rongrong.springboot.demo.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    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.jws.Oneway;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @author rongrong
     * @version 1.0
     * @description:
     * @date 2020/1/6 21:50
     */
    @Aspect
    @Component
    public class HttpAspect {
    
        /**
         * 使用self4j,此日志为spring boot自带的日志框架
         */
        private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class);
    
        /**
         *此处为了简化代码,提高维护性,还是需要提炼下的
        @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
            System.out.println("我执行了!!");
        }
         */
    
        @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))")
        public void log(){
        }
    
        /**
         * 在接口执行操作时输出相关参数
         */
        @Before("log()")
        public void doBefore(JoinPoint joinPoint){
            //使用RequestContextHolder来获得请求属性,这里需要强转成ServletRequestAttributes对象
            ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            //获取请求url
            String url = servletRequestAttributes.getRequest().getRequestURI();
            //获取请求IP
            String addr = servletRequestAttributes.getRequest().getRemoteAddr();
            //获取请求方法
            String method = servletRequestAttributes.getRequest().getMethod();
            //获取类名
            String pCName = joinPoint.getSignature().getDeclaringTypeName();
            //获取类方法
            String cName = joinPoint.getSignature().getName();
            //这里要说明下 logger.info("url={}",url),url为{}自动填充部分
            //url
            logger.info("url= {}",url);
            //ip
            logger.info("ip= {}",addr);
            //method
            logger.info("method= {}",method);
            //args
            //获取请求参数
            logger.info("args= {}",joinPoint.getArgs());
            //类名和类方法
            logger.info("类名和类方法= {}",pCName+"."+cName);
        }
    
        @After("log()")
        public void doAfter(){
            logger.info("doAfter :我执行了!!");
        }
    
        /**
         * 使用@AfterReturning,获取返回相应信息
         */
        @AfterReturning(returning = "object",pointcut="log()")
        public void doAfterReturning(Object object){
            logger.info("返回信息 :{}",object.toString());
        }
    }

    再次重新启动项目,我们调用下查询所有学生接口,控制台显示如下信息,证明日志成功!

    到此,spring boot中Aop的使用分享完毕,有兴趣的同学可以自行尝试哦。

  • 相关阅读:
    P3391 【模板】文艺平衡树(Splay)
    P4198 楼房重建
    P1491 集合位置
    P3957 跳房子
    P4016 负载平衡问题
    bzoj1077: [SCOI2008]天平 差分约束
    bzoj1151: [CTSC2007]动物园zoo 状压dp
    bzoj1076: [SCOI2008]奖励关 状压dp
    bzoj1226: [SDOI2009]学校食堂Dining 状压dp
    bzoj1879: [Sdoi2009]Bill的挑战 状压dp
  • 原文地址:https://www.cnblogs.com/longronglang/p/12158841.html
Copyright © 2011-2022 走看看