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); } } }
以上是项目中的部门真实代码。读者需要关注的有以下几点:
- 注解:@Aspect、@Pointcut、@After、@Before;
- 拦截的方法命名规则要统一,比如新增时 addXXX 或者 insertXXX,更新是 updateXXX 或者 editXXX,查询是 queryXXX 或者 selectXXX ,删除是 deleteXXX 等;
- 处理相关的业务逻辑,比如,数据入库,导出日志文件等等;
- 需要理解 Spring aop 中的相关概念,比如切点、切面、增强/通知等。SpringBoot Aop 还是对 Spring Aop 的封装。
下面是本项目中的日志展示效果:
总结
本文希望传到的不仅仅是一个解决方案,更希望传达一种思想。在需求实现时,多思考,而不是在没有思考的前提下,就横冲直撞,胡写一通。正如方腾飞所说,聪明人的几个特质:
1、问题是什么?
2、解决方案有哪些?
3、哪一个方案是所有方案中的捷径?