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

相关文章

Spring Boot整合Redis注解实现增删改查功能(Redis注解使用)

《SpringBoot整合Redis注解实现增删改查功能(Redis注解使用)》文章介绍了如何使用SpringBoot整合Redis注解实现增删改查功能,包括配置、实体类、Repository、Se... 目录配置Redis连接定义实体类创建Repository接口增删改查操作示例插入数据查询数据删除数据更

Java Lettuce 客户端入门到生产的实现步骤

《JavaLettuce客户端入门到生产的实现步骤》本文主要介绍了JavaLettuce客户端入门到生产的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录1 安装依赖MavenGradle2 最小化连接示例3 核心特性速览4 生产环境配置建议5 常见问题

使用python生成固定格式序号的方法详解

《使用python生成固定格式序号的方法详解》这篇文章主要为大家详细介绍了如何使用python生成固定格式序号,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录生成结果验证完整生成代码扩展说明1. 保存到文本文件2. 转换为jsON格式3. 处理特殊序号格式(如带圈数字)4

Java使用Swing生成一个最大公约数计算器

《Java使用Swing生成一个最大公约数计算器》这篇文章主要为大家详细介绍了Java使用Swing生成一个最大公约数计算器的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下... 目录第一步:利用欧几里得算法计算最大公约数欧几里得算法的证明情形 1:b=0情形 2:b>0完成相关代码第二步:加

Java 的ArrayList集合底层实现与最佳实践

《Java的ArrayList集合底层实现与最佳实践》本文主要介绍了Java的ArrayList集合类的核心概念、底层实现、关键成员变量、初始化机制、容量演变、扩容机制、性能分析、核心方法源码解析、... 目录1. 核心概念与底层实现1.1 ArrayList 的本质1.1.1 底层数据结构JDK 1.7

Java Map排序如何按照值按照键排序

《JavaMap排序如何按照值按照键排序》该文章主要介绍Java中三种Map(HashMap、LinkedHashMap、TreeMap)的默认排序行为及实现按键排序和按值排序的方法,每种方法结合实... 目录一、先理清 3 种 Map 的默认排序行为二、按「键」排序的实现方式1. 方式 1:用 TreeM

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.