最近做的項目,需要操作日志記錄功能,於是想到了自定義注解+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; } }
這樣日志就操作完成,下面使用該注解:
日志添加到數據庫了