1、介绍:
我们都知道AOP是Spring中的重要组成部分,作用就是在不改变业务代码的情况下对业务进行扩展,具体表现为,登录认证、日志管理、事务管理、用户信息追踪等等。而AOP切面类中可以实现的通知有: 前置通知、后置通知、异常通知、返回通知、环绕通知(最为重要,可以控制方法何时进行调用) 在本文,我将详细的介绍在Sprinboot中如何使用注解对用户的日志进行管理。
AOP的底层原理,这里提一嘴,AOP的底层原理就是动态代理,JDK动态代理和CGLIB动态代理,后面我会出Spring系列的博客讲解Spring IOC的原理和 SpringAOP的原理。
2、具体应用
- 第一步: 引入aop的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 第二步: yml文件中开启aop功能
- 第三步: 配置aop包的目录结构
- 第五步: 编码各个文件
1、SysLog实体类:
@ApiModel("日志的实体类")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysLog implements Serializable {
@ApiModelProperty(value = "创建的id",example = "0")
private String id;
@ApiModelProperty(value = "操作")
private String operation;
@ApiModelProperty(value = "方法名")
private String method;
@ApiModelProperty(value = "参数")
private String params;
@ApiModelProperty(value = "ip地址")
private String ip;
@ApiModelProperty(value = "报错信息,当无报错的时候这个信息为空")
private String errorMessage;
@ApiModelProperty(value = "用户操作返回的信息")
private String responseMessage;
@ApiModelProperty(value = "创建时间")
private String createTime;
}
2、SysLogMapper:
@Mapper
public interface SysLogMapper {
@Insert("insert into t_log(operation,method,params,ip,error_message,response_message)" +
"values(#{operation},#{method},#{params},#{ip},#{errorMessage},#{responseMessage})")
public void insertLog(SysLog sysLog);
}
3、 SysLogService、和 SysLogServiceImpl
public interface SysLogService {
/**
* 插入日志记录
* @param sysLog
*/
public void insertLog(SysLog sysLog);
}
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogMapper sysLogMapper;
@Override
public void insertLog(SysLog sysLog) {
sysLogMapper.insertLog(sysLog);
}
}
4、annotation --MyLog 注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {
String value() default "";
}
5、切面类,这些只写出了异常通知、返回通知、环绕通知、其他的通知还有前置通知、后置通知就不一一列出来了:
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
/**
* 切入点表达式
*/
@Pointcut("@annotation(com.justreading.onePanda.aop.annotation.MyLog)")
public void logPointCut(){};
/**
* 报错异常通知
*/
@AfterThrowing(value = "logPointCut()",throwing = "e")
public void saveErrorLog(JoinPoint joinPoint,Exception e){
SysLog sysLog = new SysLog();
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
MyLog myLog = method.getAnnotation(MyLog.class);
String operation = myLog.value();
String methodName = method.getName();
String className = joinPoint.getTarget().getClass().getName();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
Object[] args = joinPoint.getArgs();
sysLog.setOperation(operation);
sysLog.setMethod(className + "." + methodName);
sysLog.setIp(request.getRemoteAddr());
sysLog.setParams(JSON.toJSONString(args));
sysLog.setErrorMessage("报错:" + e);
//插入日志记录
sysLogService.insertLog(sysLog);
}
/**
* 切面 配置通知,返回的消息,正常的不切了,不然消息太多了
*/
@AfterReturning(value = "logPointCut()",returning = "ret")
public void saveReturnLog(JoinPoint joinPoint,Object ret){
SysLog sysLog = new SysLog();
//获取切入点所在的方法
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
//获取操作
MyLog myLog = method.getAnnotation(MyLog.class);
if(myLog != null){
String value = myLog.value();
sysLog.setOperation(value);
}
//获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
//获取请求的方法名
String methodName = method.getName();
sysLog.setMethod(className + "." + methodName);
//请求的参数
Object[] args = joinPoint.getArgs();
for(Object arg : args){
}
//将参数所在的数组转换成json
String params = JSON.toJSONString(args);
sysLog.setParams(params);
//获取用户的ip地址
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
sysLog.setIp(request.getRemoteAddr());
//设置返回的信息
sysLog.setResponseMessage("返回信息:" + JSON.toJSONString(ret));
//插入日志记录
sysLogService.insertLog(sysLog);
}
@Around(value = "logPointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前");
//被切入方法的执行
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕之后");
return proceed;
}
}
6、将该注解应用到相对应的方法上:
三、总结:
AOP的使用相对来说还是比较简单的、特别是注解版的AOP更加人性化只要通过一个注解就可以对类中的方法进行切入,相对xml方式更加的方便。