介绍:接下来我会把学习阶段学到的框架等知识点进行整合,每一次整合是在前一章的基础上进行的,所以后面的整合不会重复放前面的代码。每次的demo我放在结尾,本次是接着上一章的内容延续的,只增加新增的或者修改的代码。
上一章进行了Redis的整合,实现了一个用户对应一个token,用户登录失败次数锁定。
本章将整合AOP,实现请求接口后,对日志进行处理,不直接写到逻辑里面,尽量解耦,采用切面方式。
首先展示下我的目录结构:
勾选部分为本章整合后新增部分代码。
第一步:新增日志实体类
@Data @TableName(value = "syslog") @Accessors(chain = true) public class SysLog { private static final long serialVersionUID = 1L; @TableId(type = IdType.UUID) private String id;//id @TableField("operationUser") private String operationUser;//操作人 @TableField("path") private String path;//请求路径 @TableField("time") private String time;//方法执行时间 @TableField("parameter") private String parameter;//方法入参 @TableField("title") private String title;//操作方法 @TableField("action") private String action;//方法描述 @TableField("sysType") private Integer sysType;//系统类型 @TableField("opType") private Integer opType;//操作类型 public SysLog(String operationUser, String path, String time, String parameter, String title, String action, Integer sysType, Integer opType) { super(); this.operationUser = operationUser; this.path = path; this.time = time; this.parameter = parameter; this.title = title; this.action = action; this.sysType = sysType; this.opType = opType; } }
ps:此处所涉及的注解请不要漏写,关系到mybatis-plus的使用,此处id的自动设置不一定使用 IdType.UUID。
可以采用前面整合的代码生成器进行生成,我在此处就是使用代码生成器生成的,但是需要改进一些地方,不然会报错。
第二步:新增各层代码
第三步:编写切面需要依赖的注解(次重点)
ps:自定义注解,需要加上框选部分的注解,里面参数的选择在csdn上面有大佬有细讲文章。
第四步:编写切面(重点)
@Aspect @Component @EnableAsync public class SystemLogAspect { @Resource private SyslogMapper logMapper;//日志 mapper private String requestPath = null ; // 请求地址 private long startTimeMillis = 0; // 开始时间 private long endTimeMillis = 0; // 结束时间 private String user = null; // 操作人 private HttpServletRequest request = null;//请求 /** * 注解的位置 */ @Pointcut("@annotation(com.swagger.demo.config.OperationAnnotation)") public void logPointCut() {} /** * @param joinPoint * @Description 前置通知 方法调用前触发 记录开始时间,从session中获取操作人 */ @Before(value="logPointCut()") public void before(JoinPoint joinPoint){ startTimeMillis = System.currentTimeMillis(); } /** * @param joinPoint * @Description 获取入参方法参数 * @return */ public Map<String, Object> getNameAndValue(JoinPoint joinPoint) { Map<String, Object> param = new HashMap<>(); Object[] paramValues = joinPoint.getArgs(); String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames(); for (int i = 0; i < paramNames.length; i++) { if(paramValues[i] instanceof Integer || paramValues[i] instanceof String) { param.put(paramNames[i], paramValues[i]); } } return param; } /** * @param joinPoint * @Description 后置通知 方法调用后触发 记录结束时间 ,操作人 ,入参等 */ @After(value="logPointCut()") public void after(JoinPoint joinPoint) { request = getHttpServletRequest(); String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class<?> targetClass = null; try { targetClass = Class.forName(targetName); } catch (ClassNotFoundException e) { e.printStackTrace(); } Method[] methods = targetClass.getMethods(); String title; String action; Integer sysType; Integer opType; Class<?>[] clazzs; for (Method method : methods) { if (method.getName().equals(methodName)) { clazzs = method.getParameterTypes(); if (clazzs!=null&&clazzs.length == arguments.length &&method.getAnnotation(OperationAnnotation.class)!=null) { request = getHttpServletRequest(); requestPath=request.getServletPath(); HttpSession session = request.getSession(); user = session.getAttribute("userName").toString(); title = method.getAnnotation(OperationAnnotation.class).content(); action = method.getAnnotation(OperationAnnotation.class).action(); sysType = method.getAnnotation(OperationAnnotation.class).sysType(); opType = method.getAnnotation(OperationAnnotation.class).opType(); endTimeMillis = System.currentTimeMillis(); SysLog log=new SysLog(user, requestPath, (endTimeMillis-startTimeMillis)+"ms", getNameAndValue(joinPoint).toString(), title, action,sysType,opType); System.out.println("增加参数:"+log); logMapper.insert(log); // break; } } } } /** * @Description: 获取request */ public HttpServletRequest getHttpServletRequest(){ RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes)ra; HttpServletRequest request = sra.getRequest(); return request; } /** * @param joinPoint * @return 环绕通知 * @throws Throwable */ public Object around(ProceedingJoinPoint joinPoint) throws Throwable { return null; } /** * @param joinPoint * @Description 异常通知 */ public void throwing(JoinPoint joinPoint) { System.out.println("异常通知"); } }
ps:下面对该切面进行一个解释。
aspect注解表示将该类定义为切面。component注解表示将该类注册到spring容器。
pointcut注解表示定义切入点,此处表示,切入到 OperationAnnotation注解的位置,定义切入点的参数特别多,可以去细细了解一下。
before注解表示前置通知,相当于BeforeAdvice的功能,value的值为切入点的名字。
此方法是获取方法的入参。
after注解表示后置通知,即使切入点注解切入位置执行完之后执行该部分代码。此处从session里面获取当前用户,表示日志的操作人(在登录时将用户名存入session),实际使用也可以采用其他办法。
使用mybatis-plus的insert方法直接新增的记录,也可以自己写sql。
补充:改造了一下登录接口,登录成功后报错了用户名到session,如下。
ps:使用session需要在入参处加上HttpServletRequest request。
从登录的controller方法到实现类都需要加上该入参。
第五步:演示
请求登录接口
结果:
已经新增成功。
本期整合到此完毕,接下来会继续更新加强整合,尽情期待。
访问地址:http://localhost:8088/swagger-ui.html或者http://localhost:8088/doc.html