SpringBoot第二十五篇:SpringBoot与AOP

  • 2019 年 10 月 15 日
  • 筆記

作者:追梦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() + "},t")                  .append("HTTP_METHOD = {" + request.getMethod() + "},t")                  .append("IP = {" + request.getRemoteAddr() + "},t")                  .append("CLASS_METHOD = {" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "}t");            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、哪一个方案是所有方案中的捷径?