一.使用原因
需求是,在增、删、改时记录用户的ID、被操作数据的ID,并插入数据库
二、开发过程
比较坎坷。以前只是知道有这种方法,但怎么实现却是第一次。
pom中引入
<!--使用AspectJ方式注解需要相应的包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!--使用AspectJ方式注解需要相应的包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
1.编写注解@interface
package com.ufgov.util.opelog;
import java.lang.annotation.*;
/**
* @author lihhz
* @mail lihhz@yonyou.com
* @company http://www.yonyou.com
* @date 2017/7/12
*/
@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)//目标是方法
@Documented//文档生成时,该注解将被包含在javadoc中,可去掉
public @interface SysOpeLogInf {
// String id() default "-1";
String valueKey();
Class<?> clazz();
enum OpeType {
ADD, UPD, DEL
}
OpeType type() default OpeType.ADD;
}
2.编写切面
package com.ufgov.util.opelog;
import com.ufgov.entity.SysOpeLog;
import com.ufgov.entity.SysRole;
import com.ufgov.entity.SysUserInfo;
import com.ufgov.mapper.SysOpeLogMapper;
import com.ufgov.util.DateUtils;
import com.ufgov.util.MemcachedUtils;
import com.ufgov.util.ToolClass;
import com.ufgov.util.UUIDUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author lihhz
* @mail lihhz@yonyou.com
* @company http://www.yonyou.com
* @date 2017/7/12
*/
@Aspect
//@Component
public class SysOpeLogAspect {
private static final Logger logger = LoggerFactory.getLogger(SysOpeLogAspect.class);
@Autowired
private SysOpeLogMapper sysOpeLogMapper;
private String oldKey,newKey;
enum WHEN{
BEFORE,AFTER,AFTER_THROWING
}
@Pointcut("@annotation(com.ufgov.util.opelog.SysOpeLogInf)")
public void controllerAspect() {
}
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("=====SysLogAspect前置通知开始=====");
handleOpeLog(joinPoint,WHEN.BEFORE);
}
@AfterReturning(pointcut="controllerAspect()")
public void doAfter(JoinPoint joinPoint) {
System.out.println("=====SysLogAspect后置通知开始=====");
handleOpeLog(joinPoint,WHEN.AFTER);
oldKey=null;newKey=null;
}
@AfterThrowing(value="controllerAspect()",throwing="e")
public void doAfter(JoinPoint joinPoint, Exception e) {
System.out.println("=====SysLogAspect异常通知开始=====");
e.printStackTrace();
// handleOpeLog(joinPoint,WHEN.AFTER_THROWING);
}
private void handleOpeLog(JoinPoint joinPoint, WHEN w) {//,Exception e
try {
//获得注解
SysOpeLogInf logger = giveController(joinPoint);
if(logger == null)return;
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
String signatureStr = signature.toString(); // 获取目标方法签名
String methodName = signatureStr.substring(signatureStr.lastIndexOf(".") + 1,signatureStr.indexOf("("));
// String longTemp = joinPoint.getStaticPart().toLongString();
String classType = joinPoint.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Object targetObject = null;
if (method.isAnnotationPresent(SysOpeLogInf.class) && method.getName().equals(methodName)) {
// Annotation[][] annotations = method.getParameterAnnotations();
Annotation annotation = method.getAnnotation(SysOpeLogInf.class);
SysOpeLogInf l = (SysOpeLogInf) annotation;
Object[] args = joinPoint.getArgs();
for (Object tmpObject : args) {
if(l.clazz() != null && l.clazz() == tmpObject.getClass()){
targetObject = tmpObject;
}
// Object tmpObject = args[i];
// if (annotations.length > 0) {
// for (Annotation annotation : annotations[i]) {
// if (annotation.annotationType() == SysOpeLogInf.class) {
// }
// }
// }
}
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
SysUserInfo userInfo = MemcachedUtils.getUserInfo(request);
SysRole roleInfo = MemcachedUtils.getRoleInfo(request);
String opeType = "新增",desc;
boolean isContinue = false;
switch (l.type()) {
case ADD:
isContinue = addOrUpd(w,request,l,targetObject);
break;
case DEL:
isContinue = true;
opeType = "删除";
oldKey = request.getParameter(l.valueKey());
// desc = oldKey + "#删除";
break;
case UPD:
opeType = "修改";
isContinue = addOrUpd(w,request,l,targetObject);
break;
}
if(isContinue) {
desc = opeType + "#oldKey=" + oldKey;
if(l.type() != SysOpeLogInf.OpeType.DEL) {
if(!oldKey.equals(newKey)){
opeType = "新增";
}
desc = desc + "#newKey=" + newKey;
}
SysOpeLog opeLog = new SysOpeLog();
opeLog.setId(UUIDUtil.uuidRandom());
opeLog.setConfirmdesc(desc);
opeLog.setIp(ToolClass.getIp(request));
opeLog.setOpobj(oldKey);
opeLog.setOptime(DateUtils.getTime());
opeLog.setOpobjtype(opeType);
opeLog.setTs(DateUtils.getTime());
opeLog.setRole(roleInfo.getRoleName());
opeLog.setUserId(userInfo.getUserId());
opeLog.setUserName(userInfo.getUserName());
sysOpeLogMapper.insert(opeLog);
}
}
}
} catch (Exception exp) {
logger.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
private static SysOpeLogInf giveController(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(SysOpeLogInf.class);
}
return null;
}
// public void insertLogSuccess(JoinPoint jp, SysOpeLogInf logger) {}
//
// public void writeLogInfo(JoinPoint joinPoint, SysOpeLogInf opLogger)
// throws Exception, IllegalAccessException {}
private boolean addOrUpd (WHEN w,HttpServletRequest request,SysOpeLogInf l,Object targetObject) throws Exception{
boolean isContinue = false;
switch (w) {
case BEFORE:
oldKey = request.getParameter(l.valueKey());
break;
case AFTER:
if(targetObject != null){
Field field = targetObject.getClass().getDeclaredField(l.valueKey());
field.setAccessible(true); // 参数值为true,禁止访问控制检查
Object id = field.get(targetObject);
newKey = id.toString();
}else{
newKey = request.getParameter(l.valueKey());
}
isContinue = true;
break;
}
return isContinue;
}
}
3.在spring-mvc配置文件中配置
<!-- 启用aop -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="com.ufgov.util.opelog.SysOpeLogAspect" />
要记得加入对应的xmlns等。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd">
三、注意点
1.在spring配置文件中配置aspectj
需要注意的是在applicationContext中配置如下代码,那么设置将无法生效。参考资料:
http://bbs.csdn.net/topics/391049202?page=1
<!-- 启用aop -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="com.ufgov.util.opelog.SysOpeLogAspect" />
查看网上的资料,原因大概是spring mvc的影响(具体原因待查,不过应该还controller还有service的扫描位置有关 系),需要将上边的配置写在spring-mvc配置文件中
2.切面执行两次
在编写切面的时候写了注解@Component造成的。在网上查找,各种不一样的说法,暂时也不是很清楚,待查。总之,我的解决办法是注释@Component即可。参考
http://blog.csdn.net/u014396256/article/details/72771520
3.关于java的自定义注解
因为以前几乎没有使用到,所以对这一块几乎不懂。都是网上找到的资料,主要用在了编写@interface中
4.spring的execution表达式
这个比java的自定义注解了解的还少。主要是参考:
http://sishuok.com/forum/posts/list/281.html
5.在切面中获取request
代码片段如下:
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
6.拦截器
使用拦截器似乎可以实现一样的效果,还没有试过。
7.异常处理
java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut myMethod
我遇到的上述异常,解决办法是,提高aspectj的版本。网上也有说可以用降低jdk版本来解决
四、写在最后的话
1.谢谢网友的分享
以前,很少在网上发帖,都是上网查了之后哦自己笔记到本地。最近有了分享的觉悟,开始一点点的在网上分享自己遇到的问题,学到的知识。这些问题大都是借鉴了他人,加了些自己的拙见。希望对他人会有一些帮助
2.spring的拦截器及切面
功能很强大,但是使用到的机会却不多。需要自己多多实践