概述
我们希望用户在系统中的行为有一个具体的日志记录,但又不能直接去修改目标方法的代码,所以本次基于AOP技术进行用户行为日志的获取和记录。例如,记录谁在什么时间、执行了什么操作、访问了什么方法、传递了什么参数、耗时是多少,操作状态如何?
1.2 Entity设计
第一步创建日志信息存储对象,例如:
import java.util.Date; /** * 基于此对象封装用户行为日志? * 谁在什么时间执行了什么操作,访问了什么方法,传递了什么参数,访问时长是多少,状态是什么? */ @Data public class Log { private Long id; private String ip; private String username; private Date createdTime; private String operation; private String method; private String params; private Long time; private Integer status; private String error; }
1.3 Mapper对象设计
第二步定义日志数据操作对象,例如:
@Mapper public interface LogMapper { @Insert("insert into log(ip,username,created_time,operation,method,params,time,status,error) values(#{ip},#{username},#{createdTime},#{operation},#{method},#{params},#{time},#{status},#{error})") int insert(Log log); }
1.4 Service业务层设计
第三步定义日志业务操作对象,例如:
public interface LogService { void insert(Log log); }
@Service public class LogServiceImpl implements LogService { @Autowired private LogMapper logMapper; @Override public void insert(Log log) { System.out.println(log); logMapper.insert(log); } }
1.5 注解对象设计
第四步定义注解对象,用于描述切入点方法。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequiredLog { String operation(); }
1.6 日志切面设计
第五步创建日志切面对象,例如:
/** * 由Aspect注解描述的类型为一个切面类型,此类型中可以定义 * 1)切入点(要切入扩展业务逻辑的一些方法集合) * 2)通知方法(拓展业务逻辑的实现) */ @Slf4j @Aspect @Component public class LogAspect { /** * 通过@Pointcut定义切入点,@annotation是切入点表达式的一种写法, * 这里表示基于@RequriedLog注解描述的方法为切入点方法。 */ @Pointcut("@annotation(com.spring.annotation.RequiredLog)") public void doLog(){}//这个方法内部不用写任何代码,此方法只负责承载注解的定义。 /** * 通过@Around注解描述功能拓展方法,表示我们在此方法内部可以为目标业务方法进行 * 功能拓展,可以在目标业务执行之前,也可以在目标业务执行之后,添加拓展业务逻辑 * @param joinPoint * @return * @throws Throwable */ @Around("doLog()") public Object doLogAround(ProceedingJoinPoint joinPoint)throws Throwable{ int status=1;//初始状态 String error=null;//初始错误信息 long time=0L;//耗时 long t1=System.currentTimeMillis(); try{ Object result=joinPoint.proceed();//调用目标业务执行链(可能会有其它切面的拓展业务+目标方法业务) //System.out.println("@Around.After"); long t2=System.currentTimeMillis(); time=t2-t1; return result; }catch (Throwable e){ long t3=System.currentTimeMillis(); time=t3-t1; status=0;//表示出错了 error=e.getMessage(); throw e; }finally{ saveLog(joinPoint,time,status,error); } } @Autowired private LogService logService; }
总结 :
重难点分析 :
- AOP切入点及通知的使用方式
- 切入点表达式的不同以及如何选用?
- 用户行为日志记录通过什么方式以及什么操作完成的?