前几天接到临时需求,要求记录下每个人在几点对什么模块进行了什么操作。首先听到这个需求,我第一想到了就是用spring的AOP来实现,然后经过了一下午的调试弄出来了,在这里记录下在写代码是遇到的问题,以及解决方法。
首先明确需求,既然是要记录下每个人的操作,那肯定是需要将这些操作持久化的。于是创建了相应的表,实体类接下来开始写切面。
@Aspect
@Component
public class LogAopAspect {
private Logger logger = LoggerFactory.getLogger (this.getClass ());
@Autowired
private LogServiceImpl logservice;
@Pointcut("execution(* com.baidu.fpd.loanmis.controller..*.*(..))")
private void controllerAspect () {} // 定义一个切入点,说明我要切controller这个包下面的类
@Around("controllerAspect()") // 定义一个环绕通知
public Object around (ProceedingJoinPoint pjp) throws Throwable {
logger.info ("进入环绕通知");
LoanLogEntity ll = new LoanLogEntity ();// 要入库的实体类
// 拦截的实体类,就是当前正在执行的controller
Object target = pjp.getTarget ();
// 拦截的方法名称。当前正在执行的方法
String methodName = pjp.getSignature ().getName ();
// 拦截的放参数类型
Signature sig = pjp.getSignature ();
MethodSignature msig = null;
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException ("该注解只能用于方法");
}
msig = (MethodSignature) sig;
Class[] parameterTypes = msig.getMethod ().getParameterTypes ();
Object object = null;
// 获得被拦截的方法
Method method = null;
try {
method = target.getClass ().getMethod (methodName, parameterTypes);
} catch (NoSuchMethodException e1) {
e1.printStackTrace ();
}
if (null != method) {
// 判断是否包含自定义的注解,说明一下这里的SystemLog就是我自己自定义的注解
if (method.isAnnotationPresent (SystemLog.class)) {
SystemLog systemlog = method.getAnnotation (SystemLog.class);
if (!"".equals (systemlog.methods ())) {
// 拦截的方法参数
Object[] args = pjp.getArgs ();
if (systemlog.methods ().equals ("修改状态")) {
// dosomething
}
}
}
}
try {
object = pjp.proceed ();
ll.setResult ("执行成功!");
logservice.saveLog (ll);
} catch (Throwable e) {
ll.setResult ("执行失败!");
logservice.saveLog (ll);
}
} else { // 没有包含注解
object = pjp.proceed ();
}
} else { // 不需要拦截直接执行
object = pjp.proceed ();
}
logger.info ("退出环绕通知");
return object;
}
}
import java.lang.annotation.*;
@Target ({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {
// 这是我自定义的注解,需要记录的地方就在方法上面加上就是
String module () default "";
String methods () default "";
}
xml里加上
<aop:aspectj-autoproxy proxy-target-class="true" /> 开启aop
这基本上就完成了aop记录业务日志的操作
说一下遇到的问题吧,
一、首先就是动态的记录每个操作具体的内容,比如状态由XX变成了XX
这里可以看下这行代码 Object[] args = pjp.getArgs ();其实传过来的参数都在args里了,具体可以自己拿里面的参数值做一些业务判断
二、aop记录日志的时候打印了两遍日志,也就是两次进入环绕通知方法,网上查了下原因,是因为项目代码里有两个地方开启了代理,
@Component因为交给了spring管理<bean id="LogAopAspect" class="com.baidu.fpd.loanmis.aop.LogAopAspect"/>
后来把这段注释掉就可以了。
参考:http://stackoverflow.com/questions/7900905/spring-aop-advice-is-called-twice
最后这次尝试了写了一下枚举类,因为之前一直都没用过,这次用后发现真的太方便了,省了巨多代码这面这段就是
public enum UnionConfigStatus {
//状态 1新建 11已发布 12预发布 21已关闭 22 预关闭 31已删除
UC_NEW (1, "新建"),
UC_RELEASE (11, "已发布"),
UC_PRERELEASE (12, "预发布"),
UC_SHUTDOWN (21, "已关闭"),
UC_PRESHUTDOWN (22, "预关闭"),
UC_DELETE (31, "已删除");
private Integer status;
private String desc;
private UnionConfigStatus (Integer status, String desc) {
this.status = status;
this.desc = desc;
}
public static String getDesc (Integer key) {
for (UnionConfigStatus ucs : UnionConfigStatus.values ()) {
if (ucs.getStatus ().equals (key)) {
return ucs.getDesc ();
}
}
return "-";
}
public Integer getStatus () {
return status;
}
public String getDesc () {
return desc;
}
}