Maven项目学习(11)SSM项目使用Spring的AOP(后置)增强对系统的操作进行日志管理(实战)

本文主要是介绍Maven项目学习(11)SSM项目使用Spring的AOP(后置)增强对系统的操作进行日志管理(实战),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       这几天在学习Spring的AOP,然后自己做了一个功能,这个功能是这样的:用户进行对数据进行添加操作之后,系统将操作的用户的ID、操作的模块和操作的内容记录到数据库中。

 

AOP相关术语:

JoinPoint:连接点,指的是可以被拦截的点

PointCut:切入点,指的是真正被拦截的点

Advice:增强,指的是拦截切入点后需要做得事

Target:对象,指的是使用增强的那个对象类

Wearing:织入,指的是将增强应用到对象类的过程

Aspect:切面,指的是切入点与增强的组合

 

       aop实现原理其实是java动态代理,但是jdk的动态代理必须实现接口,所以spring的aop是用cglib这个库实现的,cglib使用了asm这个直接操纵字节码的框架,所以可以做到不实现接口的情况下完成动态代理。(转)

 

 

实验开始

1.数据库

logs表:(此表记录着用户操作的信息)

2.数据库操作层

实体类:

package com.myhomes.entity;import java.util.Date;public class Logs {private Integer id;private Date operationTime;private String types;private String operator;private String modules;private String operation;private String result;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getModules() {return modules;}public void setModules(String modules) {this.modules = modules;}public Date getOperationTime() {return operationTime;}public void setOperationTime(Date operationTime) {this.operationTime = operationTime;}public String getTypes() {return types;}public void setTypes(String types) {this.types = types;}public String getOperator() {return operator;}public void setOperator(String operator) {this.operator = operator;}public String getOperation() {return operation;}public void setOperation(String operation) {this.operation = operation;}public String getResult() {return result;}public void setResult(String result) {this.result = result;}
}

接口类:

package com.myhomes.dao;import com.myhomes.entity.Logs;
import org.springframework.stereotype.Repository;@Repository("logsDao")
public interface LogsDao {void insertLogs(Logs log);
}

映射实现:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.myhomes.dao.LogsDao"><resultMap id="logs" type="Logs"><result property="id" column="id" javaType="Integer"></result><result property="operationTime" column="operation_time" javaType="java.sql.Date"></result><result property="types" column="types" javaType="String"></result><result property="operator" column="operator" javaType="String"></result><result property="modules" column="modules" javaType="String"></result><result property="operation" column="operation" javaType="String"></result><result property="result" column="result" javaType="String"></result></resultMap><insert id="insertLogs" parameterType="Logs" useGeneratedKeys="true" keyColumn="id">insert into logs(operation_time,types,operator,modules,operation,result) value(#{operationTime},#{types},#{operator},#{modules},#{operation},#{result})</insert></mapper>

3.增强类

新建一个增强类,我是在服务层那里建的。

       因为操作日志是一定要知道是谁操作的,而如何获取操作者,我看有些朋友是通过HttpServletRequest获得session,再从session获得Attribute来知道用户身份的。但是个人感觉session不好,而且我的增强是放在服务层的,服务层没有web层的类,就比如这个类:HttpServletRequest,如果要用这个类,还要在服务层pom文件导入web层的pom文件,同时还将服务层的spring文件导入web层的spring文件的内容,麻烦得紧。而且这么做的话在IDEA启动tomcat还要确认导包之类什么的。。。

      这个例子我还是有用到session(等我把话说完哈),在前端把session中的用户信息放在一个公用的地方,赋值到一个hidden属性的a标签中,提交添加请求的时候获取a标签的内容(也就是用户在数据库表中唯一的id)与数据一同提交到服务器上,只要我把代码修改不要session是能做到的(就是用户登录之后在转发页面的时候带上用户的id,在每次用户需要自己的id进行提交操作时都发送给服务器,服务器处理之后再给会这个id给用户)。

package com.myhomes.biz.advice;import com.myhomes.biz.LogsBiz;
import com.myhomes.entity.Logs;
import org.aspectj.lang.JoinPoint;
import org.springframework.beans.factory.annotation.Autowired;public class LogsAdvice {@Autowiredprivate LogsBiz logsBiz;public void addOperationLog(JoinPoint joinPoint){Logs logs = new Logs();//target对象logs.setModules(joinPoint.getTarget().getClass().getSimpleName());//操作方法logs.setOperation(joinPoint.getSignature().getName());//操作者//System.out.println("后置增强:joinPoint.getArgs()[0].toString():"+joinPoint.getArgs()[0].toString());logs.setOperator(joinPoint.getArgs()[0].toString());//增强方法访问成功logs.setResult("1");logsBiz.addOperationLog(logs);}}

4.Spring文件中配置增强

最后那个配置

<aop:pointcut>标签指的是切入点,id作为切入点的唯一标识,execution定义使用要增强的目标方法;

<aop:aspect>标签指的是切面,ref属性选择ico注入的增强类。

<aop:after-returning>标签指的是“后置增强”,method属性指的是增强方法,pointcut-ref属性选择相关的切入点id。

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--声明式事务配置在业务层--><!--导入dao层配置文件--><import resource="spring-dao.xml"/><!--开启自动扫描--><context:component-scan base-package="com.myhomes.biz"/><!--aop自动代理--><aop:aspectj-autoproxy/><!--事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--声明通知,定义规则--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="get*" read-only="true"/>   <!--不用事务封装--><tx:method name="find*" read-only="true"/><tx:method name="search*" read-only="true"/><tx:method name="*" propagation="REQUIRED"/></tx:attributes></tx:advice><tx:annotation-driven transaction-manager="transactionManager"/><bean id="LogsAdvice" class="com.myhomes.biz.advice.LogsAdvice"></bean><!--通知和切入点进行关联--><aop:config><aop:pointcut id="addPointCut" expression="execution(* com.myhomes.controller.MonthCostController.add*(..))"/>    <!--第一个*号代表任意返回值--><aop:aspect ref="LogsAdvice"><!--<aop:before method="operationLog" pointcut-ref="pointcut1"></aop:before>--><aop:after-returning method="addOperationLog" pointcut-ref="addPointCut"></aop:after-returning></aop:aspect></aop:config></beans>

5.服务层

接口类:(最后那个)

package com.myhomes.biz;import com.myhomes.entity.Logs;public interface LogsBiz {void addSystemLog(Logs logs);void addLoginLog(Logs logs);void addOperationLog(Logs logs);
}

实现类:(最后那个)

package com.myhomes.biz.impl;import com.myhomes.biz.LogsBiz;
import com.myhomes.dao.LogsDao;
import com.myhomes.entity.Logs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;import java.util.Date;@Service("logsBiz")
public class LogsBizImpl implements LogsBiz {@Autowired@Qualifier(value = "logsDao")private LogsDao logsDao;public void addSystemLog(Logs logs) {logs.setOperationTime(new Date());//Systemlogs.setTypes("Sys");logsDao.insertLogs(logs);}public void addLoginLog(Logs logs) {logs.setOperationTime(new Date());logs.setTypes("Login");logsDao.insertLogs(logs);}public void addOperationLog(Logs logs) {logs.setOperationTime(new Date());//Operationlogs.setTypes("Ope");logsDao.insertLogs(logs);}
}

6.控制器层

将要用到增强的add方法,类的其他方法和add()的方法体代码省略了,以免影响观看。

package com.myhomes.controller;import com.myhomes.biz.HouseBiz;
import com.myhomes.biz.MonthCostBiz;
import com.myhomes.biz.UserService;
import com.myhomes.entity.House;
import com.myhomes.entity.MonthCost;
import com.myhomes.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Controller("monthCostController")
@RequestMapping(value = "/count")
public class MonthCostController {@Autowiredprivate HouseBiz houseBiz;@Autowiredprivate UserService userService;@Autowiredprivate MonthCostBiz monthCostBiz;@RequestMapping(value = "/add")@ResponseBody@Transactionalpublic Map<String,Object> addMonthCost(@RequestBody Map<String,String> map) throws ParseException {略略略}}

7.页面

利用session将用户id放到a标签里

8.js/jquery

注意看$.ajax({});前面,这里获取用户的id,并且跟数据一同提交到服务器。

//添加数据提交$("#count_house_btn").click(function () {//判断必填选项是否为空if (!$("#count_house_HouseId").val()||!$("#counthouse1").val()||!$("#rent").val()||!$("#waterMeter").val()||!$("#powerMeter").val()){alert("必选内容不能为空!");return false;}if ($("#rent").val()*1<200){alert("租金最低为200!");return false;}//判断水费度数是否比上月小if ($("#waterMeter").val()*1 < lastWaterMeter*1){alert("水费度数不能低于上月水费度数!");return false;}//判断电费度数是否比上月小if ($("#powerMeter").val()*1 < lastPowerMeter*1){alert("电费度数不能低于上月电费度数!");return false;}//判断本次年月日是否比上次计算的日期为同一日if ($("#counthouse1").val() === lastMonth){alert("本日已计算此房间房租,无须再次计算!");return false;}//判断本次年月日是否比上次计算的日期要早var lastmonth = lastMonth;var lastmonth2 = lastmonth.replace(/-/g,'');var thismonth = $("#counthouse1").val();var thismonth2 = thismonth.replace(/-/g,'');if (thismonth2*1 < lastmonth2*1){alert("本次计算房租时间不能比上次计算的房租时间要早!");return false;}//判断本次年月日是否比现在晚var date = new Date();var year = date.getFullYear();var month = date.getMonth()+1;var day = date.getDate();if (month < 10) {month = "0" + month;}if (day < 10) {day = "0" + day;}var nowDate = year + month + day;if (nowDate*1 < thismonth2*1){alert("日期不能比现在晚!");return false;}//判断网费是否小于0if ($("#network").val()*1 < 0){alert("网费不能为负数!");return false;}var houseId = $("#count_house_HouseId").val();var yearsMonth = $("#counthouse1").val();var rent = $("#rent").val()*1;var waterMeter2 = $("#waterMeter").val()*1;var powerMeter2 = $("#powerMeter").val()*1;var network = $("#network").val()*1;var clean = $("#clean").val()*1;var sums = $("#sum_input").val()*1;//获取上月水表数与电表数var lastWaterMeter = $("#lastWaterMeterLabel").text();var lastPowerMeter = $("#lastPowerMeterLabel").text();//用水电表差值进行水电费计算var waterCost = (waterMeter2 - lastWaterMeter) *2.5;var powerCost = (powerMeter2 - lastPowerMeter) *1.5;//提交用户idvar uid = $("#uid").text();// alert("waterCost:"+waterCost+",powerCost:"+powerCost);$.ajax({type:"post",url:"/count/add",dataType : "json",contentType : "application/json;charset=UTF-8",data:JSON.stringify({uid:uid,houseId:houseId,yearsMonth:yearsMonth,rent:rent,waterMeter:waterMeter2,powerMeter:powerMeter2,waterCost:waterCost,powerCost:powerCost,network:network,clean:clean,sum:sums,lastMonth:lastMonth}),success:function (data) {if (data.msg === "add") {$("#counthouse1").val("");$("#rent").val("");$("#waterMeter").val("");$("#powerMeter").val("");$("#network").val("");alert("添加成功!");window.location.href='/count/view';}else if (data.msg === "out") {$("#counthouse1").val("");$("#rent").val("");$("#waterMeter").val("");$("#powerMeter").val("");$("#network").val("");if(data.deposit1 >=0){alert("退房成功!需要向住户返还押金!"+data.deposit1+"元!");}else if(data.deposit2 < 0){alert("退房成功!押金不够抵押本月房租,需要向住户拿!"+data.deposit2+"元!");}window.location.href='/count/view';}else{alert("系统出错!");}}});});
});

9.演示

第一步:

结果:

交互成功,系统进行了添加的操作!

第二步:查看数据库日志表内容

        系统记录了uid=1,(operator字段中,并且有添加的数据),操作时间有,日志类型为操作型,是某个控制器模块,result是“1”表示成功。

第三步:查看用户表id字段为“1”的用户是谁

10.后言

       为什么我不仅把uid记录到数据库中,而且还把添加的信息都记录进去了?因为我个人觉得单单是记录某个人对数据库表进行添加操作的信息是不够的,需要把添加的信息都加进去会好很多。如果是用于修改的增强方法,还要把修改的那一条数据的id给加入到日志信息进去。

11.补

       如果日后要搞个日志查看的模块,这样设计operator字段内容不是很好,因为是要直接显示操作者是谁,如果只显示1大串内容交互不是很好,我修改了下代码,把操作人字段内容就仅添加操作者的id,result字段内容就直接是添加的数据内容和uid。

public class LogsAdvice {@Autowiredprivate LogsBiz logsBiz;public void addOperationLog(JoinPoint joinPoint){Logs logs = new Logs();//target对象logs.setModules(joinPoint.getTarget().getClass().getSimpleName());//操作方法logs.setOperation(joinPoint.getSignature().getName());//操作者//System.out.println("后置增强:joinPoint.getArgs()[0].toString():"+joinPoint.getArgs()[0].toString());Map<String,String> map = (Map<String, String>) joinPoint.getArgs()[0];logs.setOperator(map.get("uid"));//增强方法访问成功logs.setResult(joinPoint.getArgs()[0].toString());logsBiz.addOperationLog(logs);}
}

这篇关于Maven项目学习(11)SSM项目使用Spring的AOP(后置)增强对系统的操作进行日志管理(实战)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/186050

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个