• Spring MVC 中使用AOP 进行统一日志管理--注解实现


    1.AOP简介

    AOP称为面向切面编程

     AOP的基本概念

    (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知

    (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

    (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

    (4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

    (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

    2.为什么要这么做?

    AOP最常见的用法是进行日志管理和事务管理。现在我们重点说日志,下一节我们再介绍日志。

    加入没使用AOP日志,但是为了日常维护方便和查找错误方便,我们要输入日志的话只能使用log.info()输出,关键是每一个需要日志的地方都要进行重复操作,而且不同的人输出的日志格式或者日志信息不统一,不便于使用。所以,我们使用切面,将日志服务作为一个组件动态地切入到我们需要地方,统一管理。

    3.实现

    我是在以前搭建的Spring MVC 工程基础上实现的日志功能。

    首先在原来的基础上添加

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
         <version>1.9.1</version>
    </dependency>

    这个jar包的版本要和jdk匹配,否则会报错,jdk1.7以上必须使用1.7以上版本,为了避免报错,我直接使用了最高版本的。另外还要保证maven jar 保证有    这两个jar包

    4.项目配置

    项目配置中特别需要注意的是我是给controller层添加日志服务,而controller层是由Spring MVC 进行管理的,所以我们的日志服务组件也要确保能被spring MVC 扫到,而且要在spring MVC 配置文件中天AOP命名空间的支持,同时开启aop 支持。我之前走了很多弯路,就算因为我把这些配置都写在了spring的配置文件中,导致切面类一直无法生效。

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
      xmlns:p="http://www.springframework.org/schema/p"  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xmlns:context="http://www.springframework.org/schema/context"  
      xmlns:mvc="http://www.springframework.org/schema/mvc" 
      xmlns:aop="http://www.springframework.org/schema/aop" 
      xsi:schemaLocation="  
        http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-3.2.xsd  
        http://www.springframework.org/schema/mvc  
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/aop    
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
        >  
           
         <mvc:annotation-driven />   
         <!-- 扫描controller(controller层注入) -->  
         <context:component-scan base-package="com.lzl.sss.controller"/>
          
         <context:component-scan base-package="com.lzl.sss.aop"/> 
         
         <aop:aspectj-autoproxy proxy-target-class="true"/>  
           
         <!-- 视图解析器 -->
        <bean id="viewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/view/"> </property>
            <property name="suffix" value=".jsp"></property>
        </bean> 
        
    </beans>  

    5.切面类

    package com.lzl.sss.aop;
    
    import java.io.UnsupportedEncodingException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Enumeration;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    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;
    
    @Component
    @Aspect
    //定义切面类
    public class WebLogAspect {
        private Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
        
        //定义切入点
        @Pointcut("execution(* com.lzl.sss.controller.*.*(..))")
        public void log(){
            
        }
        
        //定义通知,方法执行前
        @Before("log()")
        public void doBefore(JoinPoint poin) throws UnsupportedEncodingException{
            logger.info("方法执行前,当前时间:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 记录下请求内容
            logger.info("请求URL : " + request.getRequestURL().toString());
            logger.info("请求方法 : " + request.getMethod());
            logger.info("IP地址 : " + request.getRemoteAddr());
            Enumeration<String> enu = request.getParameterNames();
            while (enu.hasMoreElements()) {
                String name = (String) enu.nextElement();
                logger.info("参数:{},值:{}", name,new String(request.getParameter(name).getBytes("ISO-8859-1"),"utf-8"));
            }
    
    
        }
        
        //定义通知,方法执行后
        @After("log()")
        public void after(JoinPoint poin){
            logger.info("方法执行后,当前时间:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        }
        
        //定义通知,方法返回前
        @AfterReturning(pointcut="log()",returning="returnVal")
        public void AfterReturning(JoinPoint poin){
            logger.info("方法返回前,当前时间:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        }
        
        //定义通知,抛出异常
        @AfterThrowing(pointcut="log()",throwing="error")
        public void AfterThrowing(Throwable error){
            logger.info("方法报错,当前时间:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        }
        
        //定义通知环绕型
        @Around("log()")
        public Object Around (ProceedingJoinPoint pjp) throws Throwable{
            logger.info("环绕前:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            Object obj= pjp.proceed();
            logger.info("环绕后:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            return obj;
        }
    }

    切面类有以下几个注意的点:

    (1).切点表达式。

    第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示controller包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数

    (2).环绕型通知

    环绕型通知在方法执行前后都会执行。且要添加返回类型,否则会导致连接点执行的方法无返回值。

    (3).通知类型的执行顺序

    正常情况:around--->before--->要执行的方法--->around---->after--->afterReturnning

    报错情况下:around--->before--->要执行的方法--->around---->after--->afterThrowing

    (4)注解

    @Component 注解将类声明为一个组件,并注入到spring MVC 容器中

    @Aspect 将这个bean修饰成一个切面类

    6.实现效果

  • 相关阅读:
    Sogou C++ Workflow 安装与使用例子
    Ubuntu c++ 使用mysql++ 链接mysql 使用cmake 构建
    现代cmake 从github引入三方库,使用FetchContent ( 3.14 以上版本)
    Vue3 + TypeScript 开发实践总结
    Spring MVC 学习总结(十)——Spring+Spring MVC+MyBatis框架集成(IntelliJ IDEA SSM集成)
    Spring MVC 学习总结(九)——Spring MVC实现RESTful与JSON(Spring MVC为前端提供服务)
    Spring MVC 学习总结(八)——Spring MVC概要与环境配置(IDEA+Maven+Tomcat7+JDK8、示例与视频)
    Spring MVC 学习总结(六)——Spring+Spring MVC+MyBatis框架集成
    Spring MVC 学习总结(五)——校验与文件上传
    Spring MVC 学习总结(四)——视图与综合示例
  • 原文地址:https://www.cnblogs.com/li-zhi-long/p/9391885.html
Copyright © 2020-2023  润新知