在进入主题之前,你必须对Spring 的AOP有一定的认识了解,本文还引用到一定的反射机制,请一并学之哦,谢谢大家支持!
首先,在构建好ssh框架后,我们先声明用来记录日志的实体类Log,代码如下:
package com.smartsoft.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "\"Log\"")
public class Log extends BaseModel {
@Id
@GenericGenerator(name = "idGenerator", strategy = "uuid")
@GeneratedValue(generator = "idGenerator")
@Column(name = "\"id\"", unique = true)
private String id;
/* 用戶id */
@Column(name = "\"userId\"")
private String userId;
/* 日志内容 */
@Column(name = "\"content\"",length = 400)
private String content;
/* 用户所做的操作 */
@Column(name = "\"operation\"")
private String operation;
/* 操作結果 */
@Column(name = "\"operationResult\"")
private String operationResult;
//此处省略get set方法
}
以上都带有注释就不解释了
然后对该实体对象生成相关的service和dao,这里我就不展示了,每个人都有不同的实现方法,只要提供一个能保存的接口方法就行了
主角上场了
package com.smartsoft.aop;
import java.lang.reflect.Method;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.smartsoft.model.Log;
import com.smartsoft.model.User;
import com.smartsoft.service.LogService;
import com.smartsoft.util.Constants;
/**
* 日志记录,添加、删除、修改方法AOP
*
* @author Dan
*
*/
@Aspect
@Component
public class LogAspect {
@Autowired
private LogService logService;// 日志记录Service
/**
* 添加业务逻辑方法切入点
*/
@Pointcut("execution(* com.smartsoft.service.impl.*.save*(..))")
public void insertServiceCall() {
System.out.println("-----------------save--------------------");
}
/**
* 修改业务逻辑方法切入点
*/
@Pointcut("execution(* com.smartsoft.service.impl.*.update*(..))")
public void updateServiceCall() {
System.out.println("-----------------update--------------------");
}
/**
* 刪除业务逻辑方法切入点
*/
@Pointcut("execution(* com.smartsoft.service.impl.*.delete*(..))")
public void deleteServiceCall() {
System.out.println("-----------------update--------------------");
}
/**
* 用戶添加操作日志(后置通知)
*
* @param joinPoint
* @param rtv
* @throws Throwable
*/
@AfterReturning(value = "insertServiceCall()", argNames = "rtv", returning = "rtv")
public void insertServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
User u = (User) session.getAttribute(Constants.USER_SESSION);
if (null == u) {
return;
}
// 判断参数
if (joinPoint.getArgs() == null) {// 没有参数
return;
}
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取操作内容
String opContent = adminOptionContent(joinPoint.getArgs(), methodName);
// 创建日志对象
Log log = new Log();
log.setUserId(u.getId());// 设置用戶id
log.setCreateTime(new Date());// 操作时间
log.setContent(opContent);// 操作内容
log.setOperation("添加");// 操作
System.out.println("++++++++++++:"+rtv);
// int ret = (Integer) rtv;
// if (ret >= 1) {
// log.setOperationResult("成功");
// } else {
// log.setOperationResult("失败");
// }
logService.log(log);// 添加日志
}
/**
* 用戶修改操作日志(后置通知)
*
* @param joinPoint
* @param rtv
* @throws Throwable
*/
@AfterReturning(value = "updateServiceCall()", argNames = "rtv", returning = "rtv")
public void updateServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
User u = (User) session.getAttribute(Constants.USER_SESSION);
if (null == u) {
return;
}
// 判断参数
if (joinPoint.getArgs() == null) {// 没有参数
return;
}
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取操作内容
String opContent = adminOptionContent(joinPoint.getArgs(), methodName);
// 创建日志对象
Log log = new Log();
log.setUserId(u.getId());// 设置用戶id
log.setCreateTime(new Date());// 操作时间
log.setContent(opContent);// 操作内容
log.setOperation("修改");// 操作
System.out.println("++++++++++++:"+rtv);
// int ret = (Integer) rtv;
// if (ret >= 1) {
// log.setOperationResult("成功");
// } else {
// log.setOperationResult("失败");
// }
logService.log(log);// 添加日志
}
/**
* 用戶刪除操作日志(后置通知)
*
* @param joinPoint
* @param rtv
* @throws Throwable
*/
@AfterReturning(value = "deleteServiceCall()", argNames = "rtv", returning = "rtv")
public void deleteServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
User u = (User) session.getAttribute(Constants.USER_SESSION);
if (null == u) {
return;
}
// 判断参数
if (joinPoint.getArgs() == null) {// 没有参数
return;
}
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取操作内容
String opContent = adminOptionContent(joinPoint.getArgs(), methodName);
// 创建日志对象
Log log = new Log();
log.setUserId(u.getId());// 设置用戶id
log.setCreateTime(new Date());// 操作时间
log.setContent(opContent);// 操作内容
log.setOperation("刪除");// 操作
System.out.println("++++++++++++:"+rtv);
// int ret = (Integer) rtv;
// if (ret >= 1) {
// log.setOperationResult("成功");
// } else {
// log.setOperationResult("失败");
// }
logService.log(log);// 添加日志
}
/**
* 使用Java反射来获取被拦截方法(insert、update)的参数值, 将参数值拼接为操作内容
*/
public String adminOptionContent(Object[] args, String mName) throws Exception {
if (args == null) {
return null;
}
StringBuffer rs = new StringBuffer();
rs.append(mName);
String className = null;
int index = 1;
// 遍历参数对象
for (Object info : args) {
// 获取对象类型
className = info.getClass().getName();
className = className.substring(className.lastIndexOf(".") + 1);
rs.append("[参数" + index + ",类型:" + className + ",值:");
// 获取对象的所有方法
Method[] methods = info.getClass().getDeclaredMethods();
// 遍历方法,判断get方法
for (Method method : methods) {
String methodName = method.getName();
// 判断是不是get方法
if (methodName.indexOf("get") == -1) {// 不是get方法
continue;// 不处理
}
Object rsValue = null;
try {
// 调用get方法,获取返回值
rsValue = method.invoke(info);
if (rsValue == null) {// 没有返回值
continue;
}
} catch (Exception e) {
continue;
}
// 将值加入内容中
rs.append("(" + methodName + " : " + rsValue + ")");
}
rs.append("]");
index++;
}
return rs.toString();
}
}
这里我稍作解析一下:
在LogAspect类下,我们分别定义了三个切点方法如下:
insertServiceCall()
updateServiceCall()
deleteServiceCall()
其中在其每个方法上面我用注解@Pointcut修饰,这里的"execution(* com.smartsoft.service.impl.*.save*(..))"是告诉容器这个切点的触发位置,我是项目里在service.impl位置下所有以save打头的方法作为切入点,换句话说就是只要程序运行到这些方法上,我们的aop就会启动,并运行我们在切点上定义的切面方法,这里可能有点绕,请自行消化
在每个切点方法下面,我还定义了对应的切面方法如下:
insertServiceCallCalls
updateServiceCallCalls
deleteServiceCallCalls
当然这些方法并不是与切点一一对应死的,只是我这里只定义一种而已,就是@AfterReturning,这个是后置通知,就是在被切点方法(就是相关service方法)运行完后才运行的切面方法,还有别的比方说@Before,@After,@Around,这里就不一一说明了,网上一查就明白了
最后在每个方法体了都调用了adminOptionContent进行操作数据的获取,注释都很清楚就也不说明了
除了以上还不够当然还有applicationContext.xml的一些常识小配置这里也不说了,总的来说注解方式的aop还是很好理解的,希望对大家多少有点帮助,再次感谢大家阅读,可以留言,我会认真答复的,再次感谢