使用Spring的AOP进行日志记录,对应的代码为
package cn.tiansu.eway.logAop;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import cn.tiansu.eway.annotation.SystemLog;
import cn.tiansu.eway.entity.LogFormMap;
import cn.tiansu.eway.mapper.LogMapper;
import cn.tiansu.eway.util.Common;
/**
* 切点类
*
* @author LJN
* @since 2015-05-05 Pm 20:35
* @version 1.0
*/
@Aspect
@Component
public class LogAopAction <span style="color:#ff0000;">implements Ordered</span>{
// 本地异常日志记录对象
private static final Logger logger = LoggerFactory.getLogger(LogAopAction.class);
@Inject
private LogMapper logMapper;
// Controller层切点
@Pointcut("@annotation(cn.tiansu.eway.annotation.SystemLog)")
public void controllerAspect() {
}
/**
* 操作异常记录
*
* @descript
* @param point
* @param e
* @author LJN
* @date 2015年5月5日
* @version 1.0
*/
@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint point, Throwable e) {
LogFormMap logForm = new LogFormMap();
Map<String, Object> map = null;
String user = null;
String ip = null;
Long start = 0L;
Long end = 0L;
Long time = 0L;
try {
ip = SecurityUtils.getSubject().getSession().getHost();
} catch (Exception ee) {
ip = "无法获取登录用户Ip";
}
try {
map = getControllerMethodDescription(point);
// 登录名
user = SecurityUtils.getSubject().getPrincipal().toString();
if (Common.isEmpty(user)) {
user = "无法获取登录用户信息!";
}
} catch (Exception ee) {
user = "无法获取登录用户信息!";
}
try {
start = System.currentTimeMillis();
end = System.currentTimeMillis();
time = end - start;
logForm.put("accountName", user);
logForm.put("module", map.get("module"));
logForm.put("methods", map.get("methods"));
logForm.put("description", "执行失败,原因:" + e);
logForm.put("actionTime", time);
logForm.put("userIP", ip);
logForm.put("type", map.get("type"));
logMapper.addEntity(logForm);
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint
* 切点
*/
@Around("controllerAspect()")
public Object doController(ProceedingJoinPoint point) {
Object result = null;
// 执行方法名
String methodName = point.getSignature().getName();
String className = point.getTarget().getClass().getSimpleName();
LogFormMap logForm = new LogFormMap();
Map<String, Object> map = null;
String user = null;
Long start = 0L;
Long end = 0L;
Long time = 0L;
String ip = null;
try {
ip = SecurityUtils.getSubject().getSession().getHost();
} catch (Exception e) {
ip = "无法获取登录用户Ip";
}
try {
// 登录名
user = SecurityUtils.getSubject().getPrincipal().toString();
if (Common.isEmpty(user)) {
user = "无法获取登录用户信息!";
}
} catch (Exception e) {
user = "无法获取登录用户信息!";
}
// 当前用户
try {
map = getControllerMethodDescription(point);
// 执行方法所消耗的时间
start = System.currentTimeMillis();
result = point.proceed();
end = System.currentTimeMillis();
time = end - start;
} catch (Throwable e) {
throw new RuntimeException(e);
}
try {
logForm.put("accountName", user);
logForm.put("module", map.get("module"));
logForm.put("methods", map.get("methods"));
logForm.put("type", map.get("type"));
logForm.put("description", map.get("description"));
logForm.put("actionTime", time.toString());
logForm.put("userIP", ip);
logMapper.addEntity(logForm);
// *========控制台输出=========*//
System.out.println("=====通知开始=====");
System.out.println("请求方法:" + className + "." + methodName + "()");
System.out.println("方法描述:" + map);
System.out.println("请求IP:" + ip);
System.out.println("=====通知结束=====");
} catch (Exception e) {
// 记录本地异常日志
logger.error("====通知异常====");
logger.error("异常信息:{}", e.getMessage());
}
return result;
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint
* 切点
* @return 方法描述
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public Map<String, Object> getControllerMethodDescription(
JoinPoint joinPoint) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
map.put("module", method.getAnnotation(SystemLog.class)
.module());
map.put("methods", method.getAnnotation(SystemLog.class)
.methods());
map.put("type", method.getAnnotation(SystemLog.class)
.type());
String de = method.getAnnotation(SystemLog.class)
.description();
if (Common.isEmpty(de))
de = "执行成功!";
map.put("description", de);
break;
}
}
}
return map;
}
<span style="color:#ff0000;">@Override
public int getOrder() {
// TODO Auto-generated method stub
return 1;
}</span>
}
当执行正常时,走
@Around;当执行异常时,走@AfterThrowing。结果发现,执行异常时,@AfterThrowing方法正常执行完毕,但是异常日志并没有插入数据表中,插入日志的代码也没报任何错误,遂百度之(http://my.oschina.net/HuifengWang/blog/304188),发现问题所在:
Spring中的事务是通过aop来实现的,当我们自己写aop拦截的时候,会遇到跟spring的事务aop执行的先后顺序问题,比如说动态切换数据源的问题,如果事务在前,数据源切换在后,会导致数据源切换失效,所以就用到了Order(排序)这个关键字.我们可以通过在@AspectJ的方法中实现org.springframework.core.Ordered 这个接口来定义order的顺序,order 的值越小,说明越先被执行。
当实现Ordered 接口之后,我们自己写的aop在事务介入之前就执行了!