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 @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建

SpringBoot中使用Flux实现流式返回的方法小结

《SpringBoot中使用Flux实现流式返回的方法小结》文章介绍流式返回(StreamingResponse)在SpringBoot中通过Flux实现,优势包括提升用户体验、降低内存消耗、支持长连... 目录背景流式返回的核心概念与优势1. 提升用户体验2. 降低内存消耗3. 支持长连接与实时通信在Sp

MySQL 打开binlog日志的方法及注意事项

《MySQL打开binlog日志的方法及注意事项》本文给大家介绍MySQL打开binlog日志的方法及注意事项,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、默认状态二、如何检查 binlog 状态三、如何开启 binlog3.1 临时开启(重启后失效)

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

Mac系统下卸载JAVA和JDK的步骤

《Mac系统下卸载JAVA和JDK的步骤》JDK是Java语言的软件开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源,:本文主要介绍Mac系统下卸载JAVA和JDK的相关资料,需... 目录1. 卸载系统自带的 Java 版本检查当前 Java 版本通过命令卸载系统 Java2. 卸载自定

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Java Spring ApplicationEvent 代码示例解析

《JavaSpringApplicationEvent代码示例解析》本文解析了Spring事件机制,涵盖核心概念(发布-订阅/观察者模式)、代码实现(事件定义、发布、监听)及高级应用(异步处理、... 目录一、Spring 事件机制核心概念1. 事件驱动架构模型2. 核心组件二、代码示例解析1. 事件定义

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

python使用库爬取m3u8文件的示例

《python使用库爬取m3u8文件的示例》本文主要介绍了python使用库爬取m3u8文件的示例,可以使用requests、m3u8、ffmpeg等库,实现获取、解析、下载视频片段并合并等步骤,具有... 目录一、准备工作二、获取m3u8文件内容三、解析m3u8文件四、下载视频片段五、合并视频片段六、错误

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器