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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物