spring boot 中AOP的使用

1、AOP统一处理请求日志

也谈AOP

一、AOP是一种编程范式java

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

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

 

再谈AOP

一、面向过程到面向对象spring

二、换个角度看世界,换个姿式处理问题 编程

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

 

2、处理过程

我的理解,其实就是日志体系为了方便使用,能够用log4j的思惟去理解下。框架

 

3、Aop的实际应用

一、准备工做

在pom中添加aop依赖,具体示例以下:spring-boot

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

二、日志输出

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

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

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("我执行了!!");
    }
}

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

三、日志输出代码优化

有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("我执行了!!");
    }
}

四、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.一、输出请求参数信息

使用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.二、输出响应信息

接下来咱们再来输出响应结果信息,使用注解@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的使用分享完毕,有兴趣的同窗能够自行尝试哦。

相关文章
相关标签/搜索