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

20 篇文章 0 订阅
在spring boot中,简单几步,使用spring AOP实现一个拦截器:

1、引入依赖:
  1. <dependency>  
  2.     <groupId>org.springframework.boot</groupId>  
  3.     <artifactId>spring-boot-starter-aop</artifactId>  
  4. </dependency>  
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2、创建拦截器类(在该类中,定义了拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。):
  1. /** 
  2.  * 拦截器:记录用户操作日志,检查用户是否登录…… 
  3.  * @author XuJijun 
  4.  */  
  5. @Aspect  
  6. @Component  
  7. public class ControllerInterceptor {  
  8.     private static final Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class);  
  9.       
  10.     @Value(“${spring.profiles}”)  
  11.     private String env;  
  12.       
  13.     /** 
  14.      * 定义拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。 
  15.      */  
  16.     @Pointcut(“execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)”)  
  17.     public void controllerMethodPointcut(){}  
  18.       
  19.     /** 
  20.      * 拦截器具体实现 
  21.      * @param pjp 
  22.      * @return JsonResult(被拦截方法的执行结果,或需要登录的错误提示。) 
  23.      */  
  24.     @Around(“controllerMethodPointcut()”//指定拦截器规则;也可以直接把“execution(* com.xjj………)”写进这里  
  25.     public Object Interceptor(ProceedingJoinPoint pjp){  
  26.         long beginTime = System.currentTimeMillis();  
  27.         MethodSignature signature = (MethodSignature) pjp.getSignature();  
  28.         Method method = signature.getMethod(); //获取被拦截的方法  
  29.         String methodName = method.getName(); //获取被拦截的方法名  
  30.           
  31.         Set<Object> allParams = new LinkedHashSet<>(); //保存所有请求参数,用于输出到日志中  
  32.           
  33.         logger.info(”请求开始,方法:{}”, methodName);  
  34.           
  35.         Object result = null;  
  36.   
  37.         Object[] args = pjp.getArgs();  
  38.         for(Object arg : args){  
  39.             //logger.debug(“arg: {}”, arg);  
  40.             if (arg instanceof Map<?, ?>) {  
  41.                 //提取方法中的MAP参数,用于记录进日志中  
  42.                 @SuppressWarnings(“unchecked”)  
  43.                 Map<String, Object> map = (Map<String, Object>) arg;  
  44.   
  45.                 allParams.add(map);  
  46.             }else if(arg instanceof HttpServletRequest){  
  47.                 HttpServletRequest request = (HttpServletRequest) arg;  
  48.                 if(isLoginRequired(method)){  
  49.                     if(!isLogin(request)){  
  50.                         result = new JsonResult(ResultCode.NOT_LOGIN, “该操作需要登录!去登录吗?\n\n(不知道登录账号?请联系老许。)”null);  
  51.                     }  
  52.                 }  
  53.                   
  54.                 //获取query string 或 posted form data参数  
  55.                 Map<String, String[]> paramMap = request.getParameterMap();  
  56.                 if(paramMap!=null && paramMap.size()>0){  
  57.                     allParams.add(paramMap);  
  58.                 }  
  59.             }else if(arg instanceof HttpServletResponse){  
  60.                 //do nothing…  
  61.             }else{  
  62.                 //allParams.add(arg);  
  63.             }  
  64.         }  
  65.           
  66.         try {  
  67.             if(result == null){  
  68.                 // 一切正常的情况下,继续执行被拦截的方法  
  69.                 result = pjp.proceed();  
  70.             }  
  71.         } catch (Throwable e) {  
  72.             logger.info(”exception: ”, e);  
  73.             result = new JsonResult(ResultCode.EXCEPTION, “发生异常:”+e.getMessage());  
  74.         }  
  75.           
  76.         if(result instanceof JsonResult){  
  77.             long costMs = System.currentTimeMillis() - beginTime;  
  78.             logger.info(”{}请求结束,耗时:{}ms”, methodName, costMs);  
  79.         }  
  80.           
  81.         return result;  
  82.     }  
  83.       
  84.     /** 
  85.      * 判断一个方法是否需要登录 
  86.      * @param method 
  87.      * @return 
  88.      */  
  89.     private boolean isLoginRequired(Method method){  
  90.         if(!env.equals(“prod”)){ //只有生产环境才需要登录  
  91.             return false;  
  92.         }  
  93.           
  94.         boolean result = true;  
  95.         if(method.isAnnotationPresent(Permission.class)){  
  96.             result = method.getAnnotation(Permission.class).loginReqired();  
  97.         }  
  98.           
  99.         return result;  
  100.     }  
  101.       
  102.     //判断是否已经登录  
  103.     private boolean isLogin(HttpServletRequest request) {  
  104.         return true;  
  105.         /*String token = XWebUtils.getCookieByName(request, WebConstants.CookieName.AdminToken); 
  106.         if(“1”.equals(redisOperator.get(RedisConstants.Prefix.ADMIN_TOKEN+token))){ 
  107.             return true; 
  108.         }else { 
  109.             return false; 
  110.         }*/  
  111.     }  
  112. }  
/**
 * 拦截器:记录用户操作日志,检查用户是否登录……
 * @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

测试结果:

  1. 2016-07-26 11:58:12,057:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:58) - 请求开始,方法:login  
  2. 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

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot项目可以使用AOP(面向切面编程)来统一处理Web请求日志。通过AOP,我们可以在请求处理的前后添加额外的逻辑,例如记录请求的时间、URL、参数等信息。 首先,我们需要在Spring Boot项目引入spring-boot-starter-aop依赖。这可以通过在pom.xml文件添加以下代码来实现: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 接下来,我们可以创建一个切面类,用于定义我们需要在请求处理前后执行的逻辑。我们可以使用@Aspect注解来标识该类为切面类,并使用@Before和@After注解来定义在请求处理前后执行的方法。例如,我们可以创建一个名为WebLogAspect的切面类: ```java @Aspect @Component public class WebLogAspect { @Before("execution(public * com.example.controller.*.*(..))") public void doBefore(JoinPoint joinPoint) { // 在请求处理前执行的逻辑 // 记录请求的时间、URL、参数等信息 } @After("execution(public * com.example.controller.*.*(..))") public void doAfter(JoinPoint joinPoint) { // 在请求处理后执行的逻辑 // 记录请求处理结果等信息 } } ``` 在上述代码,我们使用@Before注解定义了一个doBefore方法,在执行com.example.controller包下的所有公共方法之前执行。我们可以在该方法记录请求的相关信息。类似地,我们可以使用@After注解定义一个doAfter方法,在执行请求之后执行相应的逻辑。 最后,我们需要在Spring Boot应用程序的主类上添加@EnableAspectJAutoProxy注解,以启用AOP功能。例如: ```java @SpringBootApplication @EnableAspectJAutoProxy public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 通过以上步骤,我们就可以在Spring Boot项目使用AOP统一处理Web请求日志了。在切面类定义的方法将会在请求处理的前后执行,我们可以在这些方法添加额外的逻辑来满足项目的需求。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [详解Spring Boot使用AOP统一处理Web请求日志](https://download.csdn.net/download/weixin_38595356/12780425)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Spring Boot如何使用AOP实例解析](https://download.csdn.net/download/weixin_38683562/12726375)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值