• SpringBoot第二十五篇:SpringBoot与AOP


    作者:追梦1819
    原文:https://www.cnblogs.com/yanfei1819/p/11457867.html
    版权声明:本文为博主原创文章,转载请附上博文链接!


    引言

      作者在实际项目中碰到一个问题,就是需要在系统中加入操作日志功能。但是目前系统开发已经接近尾声,功能接口达到一百几十个。

      如果按照新手的思维(项目组中有人就这样提建议),但是这样的话,工作量之大、冗余代码之多,可想而知。对于这种需求,我们应该考虑到 Java 的面向切面编程思想,使用 Spring AOP。下面,阐述在 SpringBoot 中使用 AOP。


    在项目中的使用

      为了演示更加完整的功能,此处我将我在实际项目中的需求抽取出来:将整个系统中的操作记录(比例数据的增、删、改、登录、退出等)入库。

      为了演示实际的应用场景,本处代码不做专门的 demo 处理。将本人在真实项目中的实际代码展示出来,以便读者做更好的理解。

    以下是核心代码:

    package com.sunwin.aspect;
    
    import com.sunwin.common.ErrorCode;
    import com.sunwin.db.dao.ActionLogDao;
    import com.sunwin.db.dto.ActionLogDTO;
    import com.sunwin.exception.BusinessException;
    import com.sunwin.util.DateUtil;
    import com.sunwin.util.IPUtil;
    import com.sunwin.util.UserUtil;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    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.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * Created by 追梦1819 on 2019-07-08.
     */
    @Aspect
    @Component
    public class LogAspect {
    
        @Autowired
        private ActionLogDao actionLogDao;
    
        private final static Logger logger = LoggerFactory.getLogger(LogAspect.class);
    
        // ..表示包及子包 该方法代表controller层的所有方法
        @Pointcut(
                "execution(public * com.sunwin.web.controller.*.add*(..)) ||  " +
                "execution(public * com.sunwin.web.controller.*.insert*(..)) || " +
                "execution(public * com.sunwin.web.controller.*.update*(..)) || " +
                "execution(public * com.sunwin.web.controller.*.delete*(..))||" +
    //            "execution(public * com.sunwin.web.controller.*.login*(..))"+
                "execution(public * com.sunwin.web.controller.*.logout*(..))"
        )
        public void controllerMethod() {
        }
    
    
        @Pointcut("execution(public * com.sunwin.web.controller.*.login*(..))")
        public void afterController() {
        }
    
        @After("afterController()")
        public void afterLogRequestInfo(JoinPoint joinPoint) throws Exception {
            common(joinPoint);
        }
    
        @Before("controllerMethod()")
        public void LogRequestInfo(JoinPoint joinPoint) throws Exception {
            common(joinPoint);
        }
    
        // aop业务处理逻辑
        private void common(JoinPoint joinPoint) throws BusinessException {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            StringBuffer requestLog = new StringBuffer();
            requestLog.append("请求信息:")
                    .append("URL = {" + request.getRequestURI() + "},	")
                    .append("HTTP_METHOD = {" + request.getMethod() + "},	")
                    .append("IP = {" + request.getRemoteAddr() + "},	")
                    .append("CLASS_METHOD = {" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "}	");
    
            ActionLogDTO actionLog = new ActionLogDTO();
            actionLog.setContent("调用信息是:" + requestLog);
    
            String ip = IPUtil.getLocalIPAddress();
            actionLog.setIp(ip);
            String user = UserUtil.getUser() == null ? "" : UserUtil.getUser();
            actionLog.setOperator(user);
    
            SimpleDateFormat format = new SimpleDateFormat(DateUtil.DateFormat_yyyyMMddHHmmss);
            String now = format.format(new Date());
            actionLog.setActionDate(now);
    
            String name = joinPoint.getSignature().getName();
            // 类型、操作时间、操作者、内容、ip
            // 操作类型:1-登录;2-退出;3-新增;4-编辑;5-删除;99-未知
            if (name.startsWith("delete")) {  // 删
                actionLog.setActionType(5);
            } else if (name.startsWith("update")) { // 改
                actionLog.setActionType(4);
            } else if (name.startsWith("add") || name.startsWith("insert")) { // 增
                actionLog.setActionType(3);
            } else if (name.startsWith("login")) {
                actionLog.setActionType(1);
            } else if (name.startsWith("logout")) {
                actionLog.setActionType(2);
            } else {
                actionLog.setActionType(99);
            }
    
            int count = actionLogDao.insert(actionLog);
            if (count < 1) {
                throw new BusinessException(ErrorCode.INSERT_ERROR);
            }
        }
    }
    

      以上是项目中的部门真实代码。读者需要关注的有以下几点:

    1. 注解:@Aspect、@Pointcut、@After、@Before;
    2. 拦截的方法命名规则要统一,比如新增时 addXXX 或者 insertXXX,更新是 updateXXX 或者 editXXX,查询是 queryXXX 或者 selectXXX ,删除是 deleteXXX 等;
    3. 处理相关的业务逻辑,比如,数据入库,导出日志文件等等;
    4. 需要理解 Spring aop 中的相关概念,比如切点、切面、增强/通知等。SpringBoot Aop 还是对 Spring Aop 的封装。

    下面是本项目中的日志展示效果:


    总结

      本文希望传到的不仅仅是一个解决方案,更希望传达一种思想。在需求实现时,多思考,而不是在没有思考的前提下,就横冲直撞,胡写一通。正如方腾飞所说,聪明人的几个特质:

    1、问题是什么?

    2、解决方案有哪些?

    3、哪一个方案是所有方案中的捷径?


    ![](https://img2018.cnblogs.com/blog/1183871/201910/1183871-20191015115013786-523924507.png)
  • 相关阅读:
    No module named _tkinter
    Camera2与TextureView使用
    Collections常用方法总结
    Android插件化框架
    《战狼2》观后感——民族荣耀
    《茶马古道》观后感——朝圣之路
    点击查看大图Activity
    图片压缩代码
    《天那边》观后感——对一些现象的反思
    recyclerView的使用
  • 原文地址:https://www.cnblogs.com/yanfei1819/p/11676820.html
Copyright © 2020-2023  润新知