自己定义一个注解:
package com.cy.pj.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
String value() default "operation";
}
在需要被记录的业务层方法上添加注解,这里以改变用户的状态为例:
@RequiredLog("禁用启用")
@Override
public int validById(Integer id, Integer valid, String modifiedUser) {
//1.合法性验证
if(id == null || id < 0) {
throw new IllegalArgumentException("参数不合法:id" + id);
}
if(valid != 1 && valid != 0) {
throw new IllegalArgumentException("参数不合法");
}
if(StringUtils.isEmpty(modifiedUser)) {
throw new ServiceException("修改用户名不能为空");
}
//2.执行禁用或启用操作
int rows = 0;
try {
rows = sysUserDao.validById(id, valid, modifiedUser);
} catch (Throwable e) {
e.printStackTrace();
throw new ServiceException("底层正在维护中");
}
//3.判定结果并返回
if(rows == 0) {
throw new ServiceException("记录可能已经不存在");
}
return rows;
}
创建日志切面处理类,用于输出业务开始时间,结束时间,以及将用户行为信息录入数据库。
类上面添加@Slf4j(添加lombok依赖),然后使用log.info("...")打印日志。
在定义AOP的类中,要写@Aspect,表示这是个切面
还要加上@Component,让此AOP交给Spring 管理
Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
这里用@annotation表达式,@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义。
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
在@Around修饰的环绕通知方法中(满足切入点表达式的核心业务方法执行之前和之后执行的一个操作)获取目标方法的属性信息步骤如下(基于CGLIB库的动态代理):
Class> targetCls = jp.getTarget().getClass();
MethodSignature ms = (MethodSignature)jp.getSignature();
public abstract int com.cy.pj.sys.service.SysUserService.validById(java.lang.Integer,java.lang.Integer,java.lang.String)
System.out.println(ms.getMethod());
public int com.cy.pj.sys.service.impl.SysUserServiceImpl.validById(java.lang.Integer,java.lang.Integer,java.lang.String)
RequiredLog requiredLog = ms.getMethod().getAnnotation(RequiredLog.class);
String operation = requiredLog.value();
如果是基于JDK的动态代理,就不能通过上述方法获取到方法上的注解,因为获取到的是接口上的方法,通过反射机制,获取不到的字节码文件方法上的注解,报空指针异常
Spring boot2.x 中AOP现在默认使用的CGLIB代理,假如需要使用JDK动态代理可以在配置文件(applicatiion.properties)中进行如下配置:
spring.aop.proxy-target-class=false
Method targetMethod=
targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class);
String operation=requiredLog.value();
System.out.println("targetMethod="+targetMethod);
String targetClsName = targetCls.getName();
//String methodName = ms.getMethod().getName(); 也能获取到目标方法的方法名
String targetObjectMethodName = targetClsName + "." + ms.getName();
String params = Arrays.toString(jp.getArgs());
封装用户行为日志
SysLog sysLog = new SysLog();
sysLog.setIp(ip);
sysLog.setUsername(username);
sysLog.setTime(time);
sysLog.setOperation(operation);
sysLog.setMethod(targetObjectMethodName);
sysLog.setParams(params);
调用业务层对象方法(insertObject)将日志写入到数据层
sysLogService.insertObject(sysLog);
Entity对象类实现
定义实体对象(POJO)封装从数据库查询、插入的数据实现ORM映射
@Data(添加lombok依赖)注解在类上,会为类的所有属性自动生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@Data
public class SysLog implements Serializable {
private static final long serialVersionUID = -8799081241453681134L;
private Integer id;
//用户名
private String username;
//用户操作
private String operation;
//请求方法
private String method;
//执行时长ms
private Long time;
//IP地址
private String ip;
//创建时间
private Date createdTime;
private String params;
}
Dao实现
package com.cy.pj.sys.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.cy.pj.sys.entity.SysLog;
@Mapper
public interface SysLogDao {
int insertObject(SysLog entity);
}
Mapper文件实现:
insert into sys_logs
(username,operation,method,params,time,ip,createdTime)
values
(#{username},#{operation},#{method},#{params},#{time},#{ip},now())
Serviec接口:SysLogService
package com.cy.pj.sys.service;
import com.cy.pj.sys.entity.SysLog;
public interface SysLogService {
int insertObject(SysLog entity);
}
Service接口实现类:SysLogServiceImpl
package com.cy.pj.sys.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.vo.PageObject;
import com.cy.pj.sys.dao.SysLogDao;
import com.cy.pj.sys.entity.SysLog;
import com.cy.pj.sys.service.SysLogService;
@Service
public class SysLogServiceImpl implements SysLogService{
@Autowired
private SysLogDao sysLogDao;
@Override
public int insertObject(SysLog entity) {
int rows = sysLogDao.insertObject(entity);
return rows;
}
}