利用Spring AOP做系统日志管理(annotaction注解版)

      在进入主题之前,你必须对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还是很好理解的,希望对大家多少有点帮助,再次感谢大家阅读,可以留言,我会认真答复的,再次感谢



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。