最近做的项目,需要操作日志记录功能,于是想到了自定义注解+AOP+多线程
这是项目结构:
首先自定义注解:
import java.lang.annotation.*;
/**
* 日志注解 用于记录日志
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogRecord {
String name() default "";
}
要实现AOP首先引入AOP的依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
将自定义的注解@LogRecord 作为AOP切点
package com.zdyl.devicemanagement.aop;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zdyl.devicemanagement.annotation.LogRecord;
import com.zdyl.devicemanagement.common.utils.CommonUtil;
import com.zdyl.devicemanagement.common.utils.SystemConstants;
import com.zdyl.devicemanagement.entity.LcoUsers;
import com.zdyl.devicemanagement.log.LogManager;
import com.zdyl.devicemanagement.log.LogTaskFactory;
import com.zdyl.devicemanagement.service.LcoUsersService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
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.Map;
@Slf4j
@Aspect
@Component
public class LogRecordAop {
@Resource
private LcoUsersService lcoUsersService;
@Pointcut(value = "@annotation(com.zdyl.devicemanagement.annotation.LogRecord)")
public void cutService() {
}
@Around("cutService()")
public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
//先执行业务
Object result = point.proceed();
try {
System.out.println("------------------------logAOP------------------------------");
handle(point);
} catch (Exception e) {
e.printStackTrace();
log.error("日志记录出错!", e);
}
return result;
}
private void handle(ProceedingJoinPoint point) throws Exception {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//获取登录用户
String userName = null;
String loginId = null;
String companyid = null;
LcoUsers currentuser = (LcoUsers) request.getAttribute(SystemConstants.CURRENTUSER);
if (currentuser != null) {
userName = currentuser.getUsername();
loginId = currentuser.getPhone();
}
//url
String requestURI = request.getRequestURI();
//ip
String remoteAddr = request.getRemoteAddr(); //这个方法取客户端ip"不够好"
//请求方式
String requestMethod = request.getMethod();
//请求类名
String declaringTypeName = point.getSignature().getDeclaringTypeName();
//请求方法名
String methodName = point.getSignature().getName();
//请求参数
Object[] args = point.getArgs();
//获取操作名称
//获取拦截的方法名
Signature sig = point.getSignature();
MethodSignature msig = null;
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
msig = (MethodSignature) sig;
Object target = point.getTarget();
Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
LogRecord annotation = currentMethod.getAnnotation(LogRecord.class);
String bussinessName = annotation.name();
//获取请求参数
String str = null;
Object[] params = point.getArgs();
if (args.length > 0) {
StringBuilder sb = new StringBuilder();
for (Object param : params) {
sb.append(param);
sb.append(" & ");
}
str = sb.toString();
}
//登录日志
if (methodName.equals("login")) {
String user = str.substring(str.indexOf("("), str.indexOf(")") + 1);
user = user.replace("(", "{").replace(")", "}");
Map<String, Object> map = CommonUtil.getMap(user);
loginId = map.get("phone").toString();
QueryWrapper<LcoUsers> lcoUsersQueryWrapper = new QueryWrapper<>();
lcoUsersQueryWrapper.eq("phone", loginId);
LcoUsers lcoUsers = lcoUsersService.getOne(lcoUsersQueryWrapper);
if (lcoUsers == null)
return;
companyid = lcoUsers.getCompanyid();
userName = lcoUsers.getUsername();
}
LogManager.me().executeLog(LogTaskFactory.bussinessLog(bussinessName, userName, loginId, requestURI, remoteAddr, declaringTypeName, methodName, str, companyid));
}
}
使用多线程,开启一个线程处理日志业务,不影响我们的主业务:
使用线程池:
import java.util.TimerTask; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 日志管理器 * */ public class LogManager { //日志记录操作延时 private final int OPERATE_DELAY_TIME = 10; //异步操作记录日志的线程池 private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10); private LogManager() { } public static LogManager logManager = new LogManager(); public static LogManager me() { return logManager; } public void executeLog(TimerTask task) { executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); } }
任务创建工厂
/** * 日志操作任务创建工厂 * */ @Slf4j public class LogTaskFactory { public static TimerTask bussinessLog(final String bussinessName, final String userName, final String loginId, final String requestURI, final String remoteAddr, final String declaringTypeName, final String methodName, final String param, final String companyid) { return new TimerTask() { @Override public void run() { LcoOperationlog operationLog = LogFactory.createOperationLog(bussinessName, userName, loginId, requestURI, remoteAddr, declaringTypeName, methodName, param,companyid); try { operationLog.insert(); } catch (Exception e) { log.error("创建业务日志异常!", e); } } }; } }
日志对象操作工厂
import com.zdyl.devicemanagement.entity.LcoOperationlog; import java.util.Date; /** * 日志对象创建工厂 * */ public class LogFactory { /** * 创建操作日志 * * @author fengshuonan * @Date 2017/3/30 18:45 */ public static LcoOperationlog createOperationLog(String bussinessName, String userName, String loginId, String requestURI, String remoteAddr, String declaringTypeName, String methodName, String param, String companyid) { LcoOperationlog operationLog = new LcoOperationlog(); operationLog.setMessage(bussinessName); operationLog.setUsername(userName); operationLog.setLoginID(loginId); operationLog.setRemark(requestURI); operationLog.setIp(remoteAddr); operationLog.setController(declaringTypeName); operationLog.setAction(methodName); operationLog.setRecordtime(new Date()); operationLog.setParam(param); operationLog.setCompanyID(companyid); return operationLog; } }
这样日志就操作完成,下面使用该注解:
日志添加到数据库了