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