Aop自定义注解实现日志记录
实现对Controller层切入点,在执行Controller方法时进行日志记录
需要引入的依赖
<!-- AOP相关包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
1、配置文件:
spring-mvc.xml 增加
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy/>
<!-- 配置使Spring采用CGLIB代理 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!-- 扫描切点类组件 -->
<!-- 放置切面实现类的包路径 -->
<context:component-scan base-package="doone.util.SystemLogAspect"/>
<!-- 日志表的service的包路径 -->
<context:component-scan base-package="doone.service.TlOperateLogService"/>
<bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
</bean>
2、建立一个自定义注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//@Target说明了Annotation所修饰的对象范围
//1.CONSTRUCTOR:用于描述构造器
//2.FIELD:用于描述域
//3.LOCAL_VARIABLE:用于描述局部变量
//4.METHOD:用于描述方法
//5.PACKAGE:用于描述包
//6.PARAMETER:用于描述参数
//7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Target({ElementType.PARAMETER, ElementType.METHOD})
//@Retention定义了该Annotation被保留的时间长短
//1.SOURCE:在源文件中有效(即源文件保留)
//2.CLASS:在class文件中有效(即class保留)
//3.RUNTIME:在运行时有效(即运行时保留)
@Retention(RetentionPolicy.RUNTIME)
//@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Documented
public @interface MethodLog {
/**
* 该注解作用于方法上时需要备注信息
*/
String remarks() default "";
/**
* 用于日志类型为1的会获取方法内的参数
* @return
*/
String type() default "";
/**
* 方法内参数长度
* @return
*/
int len() default 0;
}
3、书写日志记录的Service
4、定义切面类
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.google.gson.Gson;
import doone.domain.TlOperateLogBean;
import doone.domain.User;
import doone.service.TlOperateLogService;
import net.sf.json.JSONObject;
@Aspect
@Component
public class SystemLogAspect {
// 这段代码调用了org.slf4j.LoggerFactory line:280
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal("ThreadLocal beginTime");
private static final ThreadLocal<TlOperateLogBean> logThreadLocal = new NamedThreadLocal("ThreadLocal log");
private static final ThreadLocal<User> currentUser = new NamedThreadLocal("ThreadLocal user");
private long sid; //日志主键id
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private TlOperateLogService logModelService;
/**
* Controller层切点 注解拦截
*/
@Pointcut("@annotation(doone.util.MethodLog)")
public void controllerAspect() {
logger.info("====log记录---------------");
}
/**
* 用于拦截Controller层记录用户的操作的开始时间
*
* @param joinPoint 切点
* @throws InterruptedException
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) throws InterruptedException {
logger.info("====前置通知---------------");
Date beginTime = new Date();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
beginTimeThreadLocal.set(beginTime);// 线程绑定变量(该数据只有当前请求的线程可见)
if (logger.isDebugEnabled()) {// 这里日志级别为debug
logger.debug("开始计时: {} URI: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(beginTime),
request.getRequestURI());
}
// 读取session中的用户
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
System.out.println(user);
currentUser.set(user);
}
/**
* 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@After("controllerAspect()")
public void doAfter(JoinPoint joinPoint) throws Exception {
logger.info("AOP日志,后置通知-------------------------");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Map<String, Object> returnMap = new HashMap<>();
User user = currentUser.get();
TlOperateLogBean bean = new TlOperateLogBean();
String title = "";
String IP = SystemLogAspect.getIp();//请求的IP
String requestUri = request.getRequestURI();//请求的Uri
Object[] objects = joinPoint.getArgs();
System.out.println("======objects" + objects);
Map<String, Object> rMap = getControllerMethodDescription2(joinPoint);
title = (String) rMap.get("title");
String flag = (String) rMap.get("type");
int len = (int) rMap.get("len");
int i = 0;
if("1".equals(flag)) {
for(Object object : objects) {
if(len > 0) {
if(len > i) {
returnMap.put("arg" + i, object);
}
}
i ++;
}
}
// 拦截的放参数类型
Signature sig = joinPoint.getSignature();
// 拦截的方法参数
Map<String, String[]> parameterMap = request.getParameterMap();
Iterator entries = parameterMap.entrySet().iterator();
Map.Entry entry;
String name;
String value = "";
while(entries.hasNext()) {
entry = (Map.Entry) entries.next();
name = (String) entry.getKey();
Object valueObj = entry.getValue();
if (null == valueObj) {
value = "";
} else if (valueObj instanceof String[]) {
String[] values = (String[]) valueObj;
for (String v : values) {
value = v + ",";
}
value = value.substring(0, value.length() - 1);
} else {
value = valueObj.toString();
}
returnMap.put(name, value);
}
JSONObject paramsJson = JSONObject.fromObject(new Gson().toJson(returnMap));
bean.setOperateIp(IP); //IP
bean.setOperateUrl(requestUri); //请求URL
bean.setStatus("E"); //有效状态
bean.setOperateName(title);
bean.setOperateResult("0");
bean.setInParam(paramsJson.toString());
try {
if (user != null) {
logger.error("=======" + user.getLoginName());
bean.setOperator(Long.valueOf(user.getId())); //用户id
} else {
bean.setOperator((long)-1); //用户id
}
} catch (Exception e) {
e.printStackTrace();
}
sid = logModelService.insertTlOperateLog(bean);
bean.setId(sid);
logThreadLocal.set(bean);
}
/**
* 异常通知 记录操作报错日志
*
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
logger.info("AOP日志,方法出现异常----------------");
TlOperateLogBean logModel = logThreadLocal.get();
logModel.setOperateResult("error");
logModel.setOutParam(e.toString());
logModel.setUpdateTime(new Date());
new UpdateLogThread(logModel, logModelService).start();
}
@AfterReturning(pointcut="controllerAspect()",returning="returnVal")
public void afterReturn(JoinPoint joinPoint,Object returnVal){
logger.info("AOP日志,方法正常执行");
TlOperateLogBean bean = logThreadLocal.get();
bean.setUpdateTime(new Date());
bean.setOutParam(returnVal == null ? "" : returnVal.toString());
new UpdateLogThread(bean, logModelService).start();
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint 切点
* @return 方法描述
*/
public static Map<String, Object> getControllerMethodDescription2(JoinPoint joinPoint) {
Map<String, Object> map = new HashMap<String,Object>();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
MethodLog controllerLog = method.getAnnotation(MethodLog.class);
String discription = controllerLog.remarks();
map.put("title", discription);
String type = controllerLog.type();
map.put("type", type);
int len = controllerLog.len();
map.put("len", len);
return map;
}
/**
* 获取请求ip
*/
public static String getIp() {
InetAddress ia = null;
String localip = null;
try {
ia = ia.getLocalHost();
localip = ia.getHostAddress();
} catch (Exception e) {
// TODO: handle exception
}
return localip;
}
/**
* 保存日志线程
*
* @author lin.r.x
*
*/
private class SaveLogThread implements Runnable {
private TlOperateLogBean logModel;
private TlOperateLogService logModelService;
public SaveLogThread(TlOperateLogBean logModel, TlOperateLogService logModelService) {
this.logModel = logModel;
this.logModelService = logModelService;
}
@Override
public void run() {
System.out.println("保存日志");
sid = logModelService.insertTlOperateLog(logModel);
}
}
/**
* 日志更新线程
*
* @author lin.r.x
*
*/
private static class UpdateLogThread extends Thread {
private TlOperateLogBean logModel;
private TlOperateLogService logModelService;
public UpdateLogThread(TlOperateLogBean logModel, TlOperateLogService logModelService) {
super(UpdateLogThread.class.getSimpleName());
this.logModel = logModel;
this.logModelService = logModelService;
}
@Override
public void run() {
this.logModelService.updateTlOperateLogSensitive(logModel);
}
}
}
在Controller中引用
@MethodLog(remarks="数据导入-订单数据导入")
@RequestMapping(value = "/orderInfoImport")
@ResponseBody
public Map<String,Object> orderInfoImport(HttpServletRequest request, HttpServletResponse response) {
参考文章:http://itindex.net/detail/50710-springaop-controller-service