详解Spring Boot中使用AOP统一处理Web请求日志

在spring boot中,简单几步,使用spring AOP实现一个拦截器:

1、引入依赖

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

 2、创建拦截器类(在该类中,定义了拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。):

/**
 * 拦截器:记录用户操作日志,检查用户是否登录……
 * @author XuJijun
 */
@Aspect
@Component
public class ControllerInterceptor {
  private static final Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class); 

  @Value(“${spring.profiles}”)
  private String env; 

  /**
   * 定义拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。
   */
  @Pointcut(“execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)”)
  public void controllerMethodPointcut(){} 

  /**
   * 拦截器具体实现
   * @param pjp
   * @return JsonResult(被拦截方法的执行结果,或需要登录的错误提示。)
   */
  @Around(“controllerMethodPointcut()”) //指定拦截器规则;也可以直接把“execution(* com.xjj………)”写进这里
  public Object Interceptor(ProceedingJoinPoint pjp){
    long beginTime = System.currentTimeMillis();
    MethodSignature signature = (MethodSignature) pjp.getSignature();
    Method method = signature.getMethod(); //获取被拦截的方法
    String methodName = method.getName(); //获取被拦截的方法名 

    Set<Object> allParams = new LinkedHashSet<>(); //保存所有请求参数,用于输出到日志中 

    logger.info(”请求开始,方法:{}”, methodName); 

    Object result = null; 

    Object[] args = pjp.getArgs();
    for(Object arg : args){
      //logger.debug(“arg: {}”, arg);
      if (arg instanceof Map<?, ?>) {
        //提取方法中的MAP参数,用于记录进日志中
        @SuppressWarnings(“unchecked”)
        Map<String, Object> map = (Map<String, Object>) arg; 

        allParams.add(map);
      }else if(arg instanceof HttpServletRequest){
        HttpServletRequest request = (HttpServletRequest) arg;
        if(isLoginRequired(method)){
          if(!isLogin(request)){
            result = new JsonResult(ResultCode.NOT_LOGIN, “该操作需要登录!去登录吗?\n\n(不知道登录账号?请联系老许。)”, null);
          }
        } 

        //获取query string 或 posted form data参数
        Map<String, String[]> paramMap = request.getParameterMap();
        if(paramMap!=null && paramMap.size()>0){
          allParams.add(paramMap);
        }
      }else if(arg instanceof HttpServletResponse){
        //do nothing…
      }else{
        //allParams.add(arg);
      }
    } 

    try {
      if(result == null){
        // 一切正常的情况下,继续执行被拦截的方法
        result = pjp.proceed();
      }
    } catch (Throwable e) {
      logger.info(”exception: ”, e);
      result = new JsonResult(ResultCode.EXCEPTION, “发生异常:”+e.getMessage());
    } 

    if(result instanceof JsonResult){
      long costMs = System.currentTimeMillis() - beginTime;
      logger.info(”{}请求结束,耗时:{}ms”, methodName, costMs);
    } 

    return result;
  } 

  /**
   * 判断一个方法是否需要登录
   * @param method
   * @return
   */
  private boolean isLoginRequired(Method method){
    if(!env.equals(“prod”)){ //只有生产环境才需要登录
      return false;
    } 

    boolean result = true;
    if(method.isAnnotationPresent(Permission.class)){
      result = method.getAnnotation(Permission.class).loginReqired();
    } 

    return result;
  } 

  //判断是否已经登录
  private boolean isLogin(HttpServletRequest request) {
    return true;
    /*String token = XWebUtils.getCookieByName(request, WebConstants.CookieName.AdminToken);
    if(“1”.equals(redisOperator.get(RedisConstants.Prefix.ADMIN_TOKEN+token))){
      return true;
    }else {
      return false;
    }*/
  }
}
/**
 * 拦截器:记录用户操作日志,检查用户是否登录……
 * @author XuJijun
 */
@Aspect
@Component
public class ControllerInterceptor {
  private static final Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class); 

  @Value("${spring.profiles}")
  private String env; 

  /**
   * 定义拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。
   */
  @Pointcut("execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")
  public void controllerMethodPointcut(){} 

  /**
   * 拦截器具体实现
   * @param pjp
   * @return JsonResult(被拦截方法的执行结果,或需要登录的错误提示。)
   */
  @Around("controllerMethodPointcut()") //指定拦截器规则;也可以直接把“execution(* com.xjj.........)”写进这里
  public Object Interceptor(ProceedingJoinPoint pjp){
    long beginTime = System.currentTimeMillis();
    MethodSignature signature = (MethodSignature) pjp.getSignature();
    Method method = signature.getMethod(); //获取被拦截的方法
    String methodName = method.getName(); //获取被拦截的方法名 

    Set<Object> allParams = new LinkedHashSet<>(); //保存所有请求参数,用于输出到日志中 

    logger.info("请求开始,方法:{}", methodName); 

    Object result = null; 

    Object[] args = pjp.getArgs();
    for(Object arg : args){
      //logger.debug("arg: {}", arg);
      if (arg instanceof Map<?, ?>) {
        //提取方法中的MAP参数,用于记录进日志中
        @SuppressWarnings("unchecked")
        Map<String, Object> map = (Map<String, Object>) arg; 

        allParams.add(map);
      }else if(arg instanceof HttpServletRequest){
        HttpServletRequest request = (HttpServletRequest) arg;
        if(isLoginRequired(method)){
          if(!isLogin(request)){
            result = new JsonResult(ResultCode.NOT_LOGIN, "该操作需要登录!去登录吗?\n\n(不知道登录账号?请联系老许。)", null);
          }
        } 

        //获取query string 或 posted form data参数
        Map<String, String[]> paramMap = request.getParameterMap();
        if(paramMap!=null && paramMap.size()>0){
          allParams.add(paramMap);
        }
      }else if(arg instanceof HttpServletResponse){
        //do nothing...
      }else{
        //allParams.add(arg);
      }
    } 

    try {
      if(result == null){
        // 一切正常的情况下,继续执行被拦截的方法
        result = pjp.proceed();
      }
    } catch (Throwable e) {
      logger.info("exception: ", e);
      result = new JsonResult(ResultCode.EXCEPTION, "发生异常:"+e.getMessage());
    } 

    if(result instanceof JsonResult){
      long costMs = System.currentTimeMillis() - beginTime;
      logger.info("{}请求结束,耗时:{}ms", methodName, costMs);
    } 

    return result;
  } 

  /**
   * 判断一个方法是否需要登录
   * @param method
   * @return
   */
  private boolean isLoginRequired(Method method){
    if(!env.equals("prod")){ //只有生产环境才需要登录
      return false;
    } 

    boolean result = true;
    if(method.isAnnotationPresent(Permission.class)){
      result = method.getAnnotation(Permission.class).loginReqired();
    } 

    return result;
  } 

  //判断是否已经登录
  private boolean isLogin(HttpServletRequest request) {
    return true;
    /*String token = XWebUtils.getCookieByName(request, WebConstants.CookieName.AdminToken);
    if("1".equals(redisOperator.get(RedisConstants.Prefix.ADMIN_TOKEN+token))){
      return true;
    }else {
      return false;
    }*/
  }
}

3、测试

浏览器中输入:http://localhost:8082/api/admin/login

测试结果:

2016-07-26 11:58:12,057:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:58) - 请求开始,方法:login
2016-07-26 11:58:12,061:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:103) - login请求结束,耗时:8ms 
2016-07-26 11:58:12,057:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:58) - 请求开始,方法:login
2016-07-26 11:58:12,061:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:103) - login请求结束,耗时:8ms 

证明拦截器已经生效。

源代码参考:https://github.com/xujijun/my-spring-boot

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • spring boot如何使用spring AOP实现拦截器

    在spring boot中,简单几步,使用spring AOP实现一个拦截器: 1.引入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 2.创建拦截器类(在该类中,定义了拦截规则:拦截com.xjj.web.controller包下面的所

  • 详解SpringBoot之集成Spring AOP

    在开始之前,我们先把需要的jar包添加到工程里.新增Maven依赖如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 接下来,我们进入正题.这里的涉及的通知类型有:前置通知.后置最终通知.后置返回通知.后置异常通知.环绕通知,下面我们就具体的

  • 详解SpringBoot AOP 拦截器(Aspect注解方式)

    常用用于实现拦截的有:Filter.HandlerInterceptor.MethodInterceptor 第一种Filter属于Servlet提供的,后两者是spring提供的,HandlerInterceptor属于Spring MVC项目提供的,用来拦截请求,在MethodInterceptor之前执行. 实现一个HandlerInterceptor可以实现接口HandlerInterceptor,也可以继承HandlerInterceptorAdapter类,两种方法一样.这个不在本文

  • spring-boot使用AOP统一处理日志

    AOP我想大家都很清楚,有时候我们需要处理一些请求日志,或者对某些方法进行一些监控,如果出现例外情况应该进行怎么样的处理,现在,我们从spring-boot中引入AOP. [开发环境:jdk版本号为1.8,spring boot的版本号为1.4.1]{style="background-color:#FF0000"} 首先,我们先引入jar包, POM文件添加如下内容: <!--引用AOP--> <dependency> <groupId>org.s

  • Spring Boot学习入门之AOP处理请求详解

    前言 面向切面(AOP)Aspect Oriented Programming是一种编程范式,与语言无关,是一种程序设计思想,它也是spring的两大核心之一. 在spring Boot中,如何用AOP实现拦截器呢? 首先加入依赖关系: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId>

  • 详解Spring Boot中使用AOP统一处理Web请求日志

    在spring boot中,简单几步,使用spring AOP实现一个拦截器: 1.引入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframewo

  • Spring Boot中使用AOP统一处理web层异常的方法

    在springboot错误默认是跳转到 请求返回渲染路径中的error/错误页面中. 源码分析:DefaultErrorViewResolver.java private ModelAndView resolve(String viewName, Map<String, Object> model) { String errorViewName = "error/" + viewName; TemplateAvailabilityProvider provider = th

  • spring boot如何使用AOP统一处理web请求

    为了保证服务的高可用,及时发现问题,迅速解决问题,为应用添加log是必不可少的. 但是随着项目的增大,方法增多,每个方法加单独加日志处理会有很多冗余 那在SpringBoot项目中如何统一的处理Web请求日志? 基本思想: 采用AOP的方式,拦截请求,写入日志 AOP 是面向切面的编程,就是在运行期通过动态代理的方式对代码进行增强处理 基于AOP不会破坏原来程序逻辑,因此它可以很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率.

  • SpringBoot AOP统一处理Web请求日志的示例代码

    SpringBoot AOP统一处理Web请求日志 引入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 新建过滤器 WebLogAspect.java 使用 @Aspect 注解修饰:说明当前类 是一个切面类. 使用 @Component

  • 详解Spring Boot中使用Flyway来管理数据库版本

    如果没有读过上面内容的读者,有兴趣的可以一阅.在上面的使用JdbcTemplate一文中,主要通过spring提供的JdbcTemplate实现对用户表的增删改查操作.在实现这个例子的时候,我们事先在MySQL中创建了用户表.创建表的过程我们在实际开发系统的时候会经常使用,但是一直有一个问题存在,由于一个系统的程序版本通过git得到了很好的版本控制,而数据库结构并没有,即使我们通过Git进行了语句的版本化,那么在各个环境的数据库中如何做好版本管理呢?下面我们就通过本文来学习一下在Spring B

  • 详解Spring Boot中使用@Scheduled创建定时任务

    我们在编写Spring Boot应用中经常会遇到这样的场景,比如:我需要定时地发送一些短信.邮件之类的操作,也可能会定时地检查和监控一些标志.参数等. 创建定时任务 在Spring Boot中编写定时任务是非常简单的事,下面通过实例介绍如何在Spring Boot中创建定时任务,实现每过5秒输出一下当前时间. 在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 @SpringBootApplication @EnableScheduling publi

  • 详解Spring Boot 中实现定时任务的两种方式

    在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Quartz ,Spring Boot 源自 Spring+SpringMVC ,因此天然具备这两个 Spring 中的定时任务实现策略,当然也支持 Quartz,本文我们就来看下 Spring Boot 中两种定时任务的实现方式. @Scheduled 使用 @Scheduled 非常容易,直接创建一个

  • 详解spring boot中使用JdbcTemplate

    本文将介绍如何将spring boot 与 JdbcTemplate一起工作. Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中. JdbcTemplate 是在JDBC API基础上提供了更抽象的封装,并提供了基于方法注解的事务管理能力. 通过使用SpringBoot自动配置功能并代替我们自动配置beans. 数据源配置 在maven中,我们需要增加spring-boot-starter-jdbc

  • 详解Spring Boot中Controller用法

    Controller Controller是SpringBoot里最基本的组件,他的作用是把用户提交来的请求通过对URL的匹配,分配个不同的接收器,再进行处理,然后向用户返回结果.他的重点就在于如何从HTTP请求中获得信息,提取参数,并分发给不同的处理服务. 基本组成 一个最经典的Controller应该大概长这样: package com.example.demo.controller; import org.springframework.stereotype.Controller; imp

  • 详解Spring Boot中MyBatis的使用方法

    orm框架的本质是简化编程中操作数据库的编码,发展到现在基本上就剩两家了,一个是宣称可以不用写一句SQL的hibernate,一个是可以灵活调试动态sql的mybatis,两者各有特点,在企业级系统开发中可以根据需求灵活使用.发现一个有趣的现象:传统企业大都喜欢使用hibernate,互联网行业通常使用mybatis. hibernate特点就是所有的sql都用Java代码来生成,不用跳出程序去写(看)sql,有着编程的完整性,发展到最顶端就是spring data jpa这种模式了,基本上根据

随机推荐