利用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还是很好理解的,希望对大家多少有点帮助,再次感谢大家阅读,可以留言,我会认真答复的,再次感谢

时间: 2024-10-15 15:22:58

利用Spring AOP做系统日志管理(annotaction注解版)的相关文章

化繁就简,如何利用Spring AOP快速实现系统日志

1.引言 有关Spring AOP的概念就不细讲了,网上这样的文章一大堆,要讲我也不会比别人讲得更好,所以就不啰嗦了. 为什么要用Spring AOP呢?少写代码.专注自身业务逻辑实现(关注本身的业务而不去想其它事情,如安全.事务.日志等),用点上档次的话说:通过非侵入式的方式实现我们要实现的功能. 我们为什么要用Spring AOP来实现系统日志呢?系统日志的特点是在系统的多个模块中都要用到,为了实现日志的统一管理,我们一般有以下三种做法: 定义日志实现类,在需要记录日志的地方创建实例来进行调

利用Spring AOP与JAVA注解为系统增加日志功能

Spring AOP一直是Spring的一个比较有特色的功能,利用它可以在现有的代码的任何地方,嵌入我们所想的逻辑功能,并且不需要改变我们现有的代码结构. 鉴于此,现在的系统已经完成了所有的功能的开发,我们需要把系统的操作日志记录起来,以方便查看某人某时执行了哪一些操作.Spring AOP可以方便查看到某人某时执行了哪一些类的哪一些方法,以及对应的参数.但是大部分终端用户看这些方法的名称时,并不知道这些方法名代码了哪一些操作,于是方法名对应的方法描述需要记录起来,并且呈现给用户.我们知道,AO

(转)利用Spring AOP自定义注解解决日志和签名校验

一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: boolean isValid = accountService.validSignature(appid, signature, client_signature); if (!isValid) return ErrorUtil.buildError(ErrorUtil.ERR_CODE_COM

利用Spring AOP自定义注解解决日志和签名校验

转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: boolean isValid = accountService.validSignature(appid, signature, client_signature); if (!

做一些Spring AOP做过的事,封装 jdk动态代理成为一个黑盒子

  怎么使用eclise 抽取方法,请看  利用eclipse 抽取代码片段为方法   抽取完成之后,还需要 ① 将Collection.class换成  target.getClass(),target是Object的,可以来代理所有的对象 ② Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHander(){     invo

利用 yEd 软件做元数据管理

yEd Diagram editor 是我常用的 flow chart 制图工具, 另外我也用它画 ER 和 use case 图. 总结一下我喜欢 yEd 的原因:1. 出色的对齐功能2. 可随意拖动Node, 永远不用担心相连的 Edge 会自动断开连接3. 每个 Node 都自带一个Label, 加说明文字非常方便4. 每个 Edge 都自带一个Label, 加说明文字非常方便 今天总结的是一个非常有价值的使用场景, 在数据仓库和大数据平台中, 数据表的关系很复杂,随着平台的不断建设, 到

IOC——Spring的bean的管理(注解方式)

注解(简单解释) 1.代码里面特殊标记,使用注解可以完成一定的功能 2.注解写法 @注解名称(属性名称=属性值) 3.注解使用在类上面,方法上面和属性上面 注意:注解方式不能完全替代配置文件方式 Spring注解开发准备工作 1.引入Jar包(Maven项目) pom文件 <dependencies> <!-- 引入一个spring-context 会自动依赖 spring-core.spring-beans.spring-expression 三个核心包 以及spring-aop.ao

Java--通过Spring AOP进行事务管理

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/t

spring:利用Spring AOP 使日志输入与方法分离

对方法进行日志输出是一种很常见的功能.传统的做法是把输出语句写在方法体的内部,在调用该方法时,用输入语句输出信息来记录方法的执行! 1.先写一个普通类: package com.importnew; public class Common { public void execute(String username,String password){ System.out.println("------------------执行 execute()方法----------------"