SpringBoot基于AOP拦截请求打印日志

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HcJsJqJSSM/article/details/84893851

一:需求背景.

      1.1 现在需要在特定的方法执行前后,做一些日志处理,尽可能不要写重复代码,优雅的完成处理方法日志打印.

      1.2 简单实现分析:如果在一些方法里面都打日志,这种方式是最易使用,但是从可维护,可扩展,耦合度来分析确实差的.

      1.3 集中处理方式:既然系统中的异常都全局处理,也是用AOP全局集中拦截处理日志,岂不是更好一下呐.定义切面功能,定义切点很切指定的方法呗,前置和后置,以及环绕,以及获得返回值.

     AOP:面向切面编程,横切面向对象中的封装类的通用的封装功能.一句话,就是将面向对象构成的庞大的体系结构中,特定需要的部分进行水平切分.

    无论是HttpRequest请求,还是数据库操作请求,都有记录日志的操作处理,将这些公用的日志功能很切出来,进行相应的前置和后置处理.

二:实战

     2.1:现在拦截指定的方法,获取请求的url,method,ip,处理该请求的方法,处理该请求的方法的参数,处理该请求返回的值.

     2.2:引入AOP依赖.(SpringBoot已经帮助我们封装好了一个starter,开箱即用)

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

  2.3 定义一个基于Http的切面.

 @Aspect注解:定义一个切面:(需要横切面向对象的功能)

@Aspect
@Component
public class HttpAspect {}

@Pointcut注解:定义一个切点:(需要横切的位置)


@Pointcut("execution(* com.lixing.docker.dockerboot.controller.TestController.aop(..))") public void log(){ 

}

​

@Before注解:定义前置通知(需要在切点方法的前面需要执行的动作处理).

扫描二维码关注公众号,回复: 4801585 查看本文章

 注意导包的正确.

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.servlet.http.HttpServletRequest;
    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
        logger.info("前置通知日志输出");
        ServletRequestAttributes attributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request=attributes.getRequest();
        // 获取url
        logger.info("url={}", request.getRequestURL());
        // 获取请求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());
    }

{}表示占位就是将后面的内容输入的位置.

说一下AspectJ中的exection表达式:

execution(* com.sample.service.impl..*.*(..))
第一个*表示方法返回值的类型任意.
第二个中间的一连串包名表示路径.
包名后面的表示..表示当前包及子包.
第二个星号表示的是所有的类.
.*(**)表示的是任何方法名,括号里面表示参数,两个点表示的是任意参数类型.

更多详细的使用请看这篇文章总结的比较全面:AOP(execution表达式)

获取Http请求的相关信息使用了Spring框架的RequestContextHolder.跟一下源码看一下.

@After:后置通知.就是请求处理方法执行完后执行的相关处理.IDEA的很形象.

获得很切请求方法的返回值.

@AfterReturning(returning=", pointcut="")

returning:返回值的类型.

pointcut:切点名称.

现在执行测试一下:非常简单的一个路径请求.

Spring获取Http请求相关的参数如下:

application.properties配置一下ContextPath.

server.context-path=/dockerboot
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
 * author: 
 * date: 
 * time: 
 * description: 获取请求的各个参数
 */
@RestController
@RequestMapping("/http")
public class HttpController {
   @RequestMapping("/attribute")
    public Map<String, Object> getAttribute(String name){
       Map<String, Object> map=new HashMap();
       ServletRequestAttributes attributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
       HttpServletRequest request=attributes.getRequest();
       map.put("url:", request.getRequestURL());
       map.put("method:", request.getMethod());
       map.put("ContextPath", request.getContextPath());
       map.put("LocalIP", request.getLocalAddr());
       map.put("ServletPath", request.getServletPath());
       map.put("AuthType", request.getAuthType());
       map.put("SessionId", request.getSession().getId());
       map.put("Session is new", request.getSession().isNew());
       map.put("Host", request.getHeader("Host"));
       map.put("User-Agent", request.getHeader("User-Agent"));
       map.put("Connection", request.getHeader("Connection"));
       map.put("Cache-Control", request.getHeader("Cache-Control"));
       map.put("Accept-Language", request.getHeader("Accept-Language"));
       map.put("Protocol", request.getProtocol());
       map.put("LocalPort", request.getLocalPort());
       map.put("RemotePort", request.getRemotePort());
       map.put("Query", request.getQueryString());
       map.put("ContentLength", request.getContentLength());
       map.put("Encoding", request.getCharacterEncoding());
       return map;
   }
}

浏览器请求如下:

这样便简单的实现了拦截请求日志,实现了控制层和日志处理的分离,降低了耦合度,非常便于维护,新增其他的日志处理也不需要额外修改控制器的功能,实现了只增强,不修改.

猜你喜欢

转载自blog.csdn.net/HcJsJqJSSM/article/details/84893851