本人是spring boot项目,相关注解依赖也已经提前导入OK,下面是实现注解需要的依赖。
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
首先需要创建一个实体类:
`package com.abcd.resdir.system.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
import com.wondersgroup.resdir.common.annotation.Excel;
import com.wondersgroup.resdir.common.core.domain.BaseEntity;
/**
* 操作日志记录表 oper_log
*
* @author wondersgroup
*/
public class SysOperLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 日志主键 */
@Excel(name = "操作序号")
private Long operId;
/** 操作模块 */
@Excel(name = "操作模块")
private String title;
/** 业务类型(0其它 1新增 2修改 3删除) */
@Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
private Integer businessType;
/** 业务类型数组 */
private Integer[] businessTypes;
/** 请求方法 */
@Excel(name = "请求方法")
private String method;
/** 操作类别(0其它 1后台用户 2手机端用户) */
@Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
private Integer operatorType;
/** 操作人员 */
@Excel(name = "操作人员")
private String operName;
/** 部门名称 */
@Excel(name = "部门名称")
private String deptName;
/** 请求url */
@Excel(name = "请求地址")
private String operUrl;
/** 操作地址 */
@Excel(name = "操作地址")
private String operIp;
/** 操作地点 */
@Excel(name = "操作地点")
private String operLocation;
/** 请求参数 */
@Excel(name = "请求参数")
private String operParam;
/** 操作状态(0正常 1异常) */
@Excel(name = "状态", readConverterExp = "0=正常,1=异常")
private Integer status;
/** 错误消息 */
@Excel(name = "错误消息")
private String errorMsg;
/** 操作时间 */
@Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date operTime;
public Long getOperId()
{
return operId;
}
public void setOperId(Long operId)
{
this.operId = operId;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public Integer getBusinessType()
{
return businessType;
}
public void setBusinessType(Integer businessType)
{
this.businessType = businessType;
}
public Integer[] getBusinessTypes()
{
return businessTypes;
}
public void setBusinessTypes(Integer[] businessTypes)
{
this.businessTypes = businessTypes;
}
public String getMethod()
{
return method;
}
public void setMethod(String method)
{
this.method = method;
}
public Integer getOperatorType()
{
return operatorType;
}
public void setOperatorType(Integer operatorType)
{
this.operatorType = operatorType;
}
public String getOperName()
{
return operName;
}
public void setOperName(String operName)
{
this.operName = operName;
}
public String getDeptName()
{
return deptName;
}
public void setDeptName(String deptName)
{
this.deptName = deptName;
}
public String getOperUrl()
{
return operUrl;
}
public void setOperUrl(String operUrl)
{
this.operUrl = operUrl;
}
public String getOperIp()
{
return operIp;
}
public void setOperIp(String operIp)
{
this.operIp = operIp;
}
public String getOperLocation()
{
return operLocation;
}
public void setOperLocation(String operLocation)
{
this.operLocation = operLocation;
}
public String getOperParam()
{
return operParam;
}
public void setOperParam(String operParam)
{
this.operParam = operParam;
}
public Integer getStatus()
{
return status;
}
public void setStatus(Integer status)
{
this.status = status;
}
public String getErrorMsg()
{
return errorMsg;
}
public void setErrorMsg(String errorMsg)
{
this.errorMsg = errorMsg;
}
public Date getOperTime()
{
return operTime;
}
public void setOperTime(Date operTime)
{
this.operTime = operTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("operId", getOperId())
.append("title", getTitle())
.append("businessType", getBusinessType())
.append("businessTypes", getBusinessTypes())
.append("method", getMethod())
.append("operatorType", getOperatorType())
.append("operName", getOperName())
.append("deptName", getDeptName())
.append("operUrl", getOperUrl())
.append("operIp", getOperIp())
.append("operLocation", getOperLocation())
.append("operParam", getOperParam())
.append("status", getStatus())
.append("errorMsg", getErrorMsg())
.append("operTime", getOperTime())
.toString();
}
}
这就是要插入到数据库的实体类,用来接收操作的信息。
`下面是自定义的注解类。
package com.abcd.resdir.Annotation;
import java.lang.annotation.*;
/**
* 自定义日志注解
* @author liuyazhuang
*
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
* 首先是被调用的方法的名称,其默认值是“”
* @return
*/
String value() default "";
/**
* 要执行的具体操作比如:菜单名称
*/
String menuName() default "";
}`
然后就是我们主要的切面类:
package com.abcd.resdir.Annotation;
import com.wondersgroup.resdir.common.core.domain.AjaxResult;
import com.wondersgroup.resdir.framework.bean.CurrentUser;
import com.wondersgroup.resdir.framework.util.ShiroUtils;
import com.wondersgroup.resdir.system.domain.SysOperLog;
import com.wondersgroup.resdir.system.service.ISysOperLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.regex.Pattern;
@Aspect
@Component
public class SystemLogAspect {
@Resource
private ISysOperLogService systemLogService;
//Controller层切点
@Pointcut("@annotation(com.abcd.resdir.Annotation.SysLog)")
public void controllerAspect() {
}
@AfterReturning(returning="ret", pointcut="controllerAspect()")
public void afterReturn(JoinPoint joinPoint,Object ret) throws Throwable{
System.out.println("=====执行controller后置返回通知=====");
//保存日志
saveSysLog(joinPoint,ret);
}
/**\
* /日志处理方法
* @param joinPoint
* @param
*/
private void saveSysLog(JoinPoint joinPoint,Object ret) {
try{
// 获取当前操作的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysOperLog log = new SysOperLog();
SysLog syslog = method.getAnnotation(SysLog.class);
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
//set方法名
Object[] args = joinPoint.getArgs();
String params = "";
StringBuffer stringBuffer = new StringBuffer();
if(args.length > 0 ){
for (int i = 0 ; i < args.length; i++){
params = args[i].toString();
stringBuffer.append(params);
}
//参数
log.setOperParam(stringBuffer.toString());
}
//方法名
log.setMethod(methodName + "()");
Class targetClass = Class.forName(className);
Method[] methods = targetClass.getMethods();
for (Method me : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = me.getParameterTypes();
if (clazzs.length == args.length) {
//获取注解上的参数
log.setBusinessType(getOperBusinessType(syslog.value()));
log.setTitle(syslog.menuName());
break;
}
}
}
// 获取 ip 地址
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//set地址IP
log.setOperIp(request.getRemoteAddr());
//set请求URL
log.setOperUrl(request.getServletPath());
CurrentUser user = ShiroUtils.getSysUser();
if( user != null){
log.setOperName(user.getUserName());
}
AjaxResult ajaxResult = (AjaxResult) ret;
if(ajaxResult.getCode() == 0){
log.setStatus(0);
}else {
log.setStatus(1);
log.setErrorMsg(ajaxResult.getMsg());
}
log.setOperatorType(0);
log.setOperTime(new Date());
// 保存日志
systemLogService.insertOperlog(log);
}catch (Exception e){
e.printStackTrace();
}
}
public static Integer getOperBusinessType(String content){
String add = ".*新增.*";
String update = ".*修改.*";
String delete = ".*删除.*";
if(Pattern.matches(add, content)){ return 1; }
if(Pattern.matches(update, content)){ return 2; }
if(Pattern.matches(delete, content)){ return 3; }
else { return 0; }
}
}
上面只写了一个@AfterReturning注解,他的作用是在使用注解的controller方法在return后才执行,还有其他的@Before,@After之类的都是跟这个差不多的原理,除了@around,方法的参数类型是ProceedingJoinPoint而不是JoinPoint。
接下里只要在controller里的方法使用此注解即可。
`@RequestMapping(value = "/batchSetLevel" ,method = RequestMethod.POST )
@ResponseBody
@SysLog(value = "批量修改用户级别",menuName = "用户管理")
public AjaxResult batchSetLevel(@RequestBody Map<String ,Object> map){
return userService.batchSetLevel(map);
}
@RequestMapping(value = "/setBatchAuthCode" ,method = RequestMethod.POST )
@ResponseBody
@SysLog(value = "批量修改用户授权码",menuName = "用户管理")
public AjaxResult setBatchAuthCode(@RequestBody Map<String ,Object> map){
return userService.setBatchAuthCode(map);
}`
本项目使用的是mybatis的ORM框架。所以插入方法在XML中,没有贴出啦。
执行完操作后控制台输出的信息,表示成功插入操作日志:
18:45:43.788 [http-nio-8090-exec-13] DEBUG c.w.r.b.m.A.updateUserStatusZero - [debug,159] - <== Updates: 5
=====执行controller后置返回通知=====
18:45:43.789 [http-nio-8090-exec-13] DEBUG c.w.r.s.m.S.insertOperlog - [debug,159] - ==> Preparing: insert into SYS_OPER_LOG (OPER_ID, TITLE, BUSINESS_TYPE, METHOD, OPERATOR_TYPE, OPER_NAME, DEPT_NAME, OPER_URL, OPER_IP, OPER_LOCATION,OPER_PARAM,STATUS,ERROR_MSG,OPER_TIME) values (SEQ_SYS_OPER_LOG.NEXTVAL,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
18:45:43.790 [http-nio-8090-exec-13] DEBUG c.w.r.s.m.S.insertOperlog - [debug,159] - ==> Parameters: 用户管理(String), 1(Integer), getUserListForSys()(String), 0(Integer), 单点测试(String), null, /User/sysUser(String), 127.0.0.1(String), null, null, 0(Integer), null, 2019-08-26 18:45:43.788(Timestamp)
18:45:43.792 [http-nio-8090-exec-13] DEBUG c.w.r.s.m.S.insertOperlog - [debug,159] - <== Updates: 1