当前位置 博文首页 > 盛夏温暖流年:使用AOP给springboot项目添加日志

    盛夏温暖流年:使用AOP给springboot项目添加日志

    作者:[db:作者] 时间:2021-07-13 19:06

    AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。一般来说,我们项目中的日志几乎会遍布所有controller,不利于统一管理,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    使用”横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

    接下来,我们看一下如何使用AOP给springboot项目添加日志:

    一.引入AOP的依赖包

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

    二.添加AOP的全局类

    @Aspect
    @Component
    public class WebLogAspect {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private LogService logService;
    
        @Autowired
        private UserService userService;
    
        private String url;
    
        private String ip;
    
        private Integer port;
    
        private User user;
    
        @Pointcut("execution(public * com.ytint.bb6.controller..*.*(..))")
        public void webLog() {
        }
    
        @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 记录请求内容,这里只记录非GET请求的日志信息
            if (!request.getMethod().equals("GET")) {
                logger.info("请求URL: " + request.getRequestURL().toString());
                url = request.getRequestURI();
                ip = request.getRemoteHost();
                port = request.getRemotePort();
                user = userService.getCurrentUser();
            }
        }
    
        @AfterReturning(returning = "ret", pointcut = "webLog()")
        public void doAfterReturning(Object ret) throws Throwable {
            // 处理完请求,返回内容
            Integer logType = LogType.OPERATION.getId();
            if (ret instanceof ResultInfo) {
                ResultInfo result = (ResultInfo) ret;
                String msg = result.getMsg();
                String module = result.getModule();
                Integer code = result.getCode();
                if (UtilValidate.isNotEmpty(msg)) {
                    logger.info(msg);
                    // 操作模块返回200才存储到日志表
                    if (code.equals(200)) {
                        logService.save(user, ip, port, msg, logType, module, url);
                    }
                }
            }
    
        }
    
        @AfterThrowing(pointcut = "webLog()", throwing = "e")
        public void doException(JoinPoint jp, Throwable e) {
            if (e != null) {
                Logger logger = LoggerFactory.getLogger(jp.getSignature().getClass());
                logger.error("程序发生了异常:" + e.getMessage(), e);
            }
        }
    }

    Spring使用AspectJ注解来声明通知方法

    @After:会在目标方法返回或抛出异常后调用

    @AfterReturning:会在目标方法返回后调用

    @AfterThrowing:会在目标方法抛出异常后调用

    @Around:会将目标方法封装起来

    @Before:会在目标方法调用之前执行

    使用@Pointcut定义切面

    @Pointcut后面括号里的内容是切面表达式:

    @Pointcut("execution(public * com.yt.controller..*.*(..))")

    表达式含义如下:

    1) execution(): 表达式主体;

    2) public *:表示返回类型, *号表示所有的类型;

    3) 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.yt.controller包、子孙包下所有类的方法;

    4) 第二个*号:表示类名,*号表示所有的类;

    5) *(..):表示方法名,*号表示所有的方法,括号里面表示方法的参数,..表示任何参数;

    webLog()方法定义了一个可重用的切点,这样我们就不必把同样的切点表达式傻傻的写好多遍了,只要在每次需要的时候引用它就可以了,webLog方法的实际内容并不重要,他本身只是一个标识,供@Pointcut注解依附。

    在日志中添加模块信息

    如果我们不只想获取当前方法的URL,参数等基本信息,还想获取它来源于某一个controller模块该怎么办呢,我这里采用了一个有点傻的方法,可以在返回的结果对象中添加一个模块字段module,把对应的controller信息放在模块里,这样输出日志时就可以知道当前请求是来源于哪个controller了。如果大家有更好的方式,欢迎交流~

    本篇博客部分参考了以下两篇博客,送上花花~~~

    https://www.cnblogs.com/hongwz/p/5764917.html
    https://www.cnblogs.com/30go/p/8443522.html

    cs
    下一篇:没有了