spring+mybatis基于 AOP实现业务日志管理
最近在项目上用到了操作日志的相关,之前的解决方案就是自己写一个日志project,然后统一调用日志接口即可,这样方便自定义定制,因为有很多设备控制之类的都是需要确认一下的,但是,对数据的操作,比如,增删改查还用这个,就有点多余了,感觉太麻烦了,所以,想想有没有日志管理这样的框架可以调用,然后就想到了spring的AOP。
整个项目的结构如下
第一步,定义日志类
package com.unisits.zngkpt.framework.logframe.pojo; import org.springframework.stereotype.Component; @Component
public class LogOperation extends LogOperationKey {
private String operatorId; private String comment; private String operatorResult; private Integer transTag; public String getOperatorId() {
return operatorId;
} public void setOperatorId(String operatorId) {
this.operatorId = operatorId;
} public String getComment() {
return comment;
} public void setComment(String comment) {
this.comment = comment;
} public String getOperatorResult() {
return operatorResult;
} public void setOperatorResult(String operatorResult) {
this.operatorResult = operatorResult;
} public Integer getTransTag() {
return transTag;
} public void setTransTag(Integer transTag) {
this.transTag = transTag;
}
}
操作类
因为用mybatis的generate来生成model,所以,这里上面的操作类继承了其主键类,
package com.unisits.zngkpt.framework.logframe.pojo; import org.springframework.stereotype.Component; import java.util.Date;
@Component
public class LogOperationKey {
private Integer unitId; private Integer deviceId; private Integer deviceType; private Date endTime; public Integer getUnitId() {
return unitId;
} public void setUnitId(Integer unitId) {
this.unitId = unitId;
} public Integer getDeviceId() {
return deviceId;
} public void setDeviceId(Integer deviceId) {
this.deviceId = deviceId;
} public Integer getDeviceType() {
return deviceType;
} public void setDeviceType(Integer deviceType) {
this.deviceType = deviceType;
} public Date getEndTime() {
return endTime;
} public void setEndTime(Date endTime) {
this.endTime = endTime;
}
}
主键类
第二步,定义日志的DAO和日志的Service
package com.unisits.zngkpt.framework.logframe.mapper; import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
import org.springframework.stereotype.Repository; /**
* @author:lyy
* @Date: 2017/6/25 10:33
* @version:
* @Description:
*/
@Repository("logOperationDao")
//@Component
public interface LogOperationDao {
/**
* @author: lyy
* @Time: 2017/6/25 10:35
* @Descrption: 插入操作类信息
* @param logOperation
* @return
* @throws
*/
public int insertLogOperation(LogOperation logOperation);
}
日志DAO接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.unisits.zngkpt.framework.logframe.mapper.LogOperationDao" >
<resultMap id="BaseResultMap" type="com.unisits.zngkpt.framework.logframe.pojo.LogOperation" >
<id column="unit_id" property="unitId" jdbcType="INTEGER" />
<id column="device_id" property="deviceId" jdbcType="INTEGER" />
<id column="device_type" property="deviceType" jdbcType="INTEGER" />
<id column="end_time" property="endTime" jdbcType="TIMESTAMP" />
<result column="operator_id" property="operatorId" jdbcType="VARCHAR" />
<result column="comment" property="comment" jdbcType="VARCHAR" />
<result column="operator_result" property="operatorResult" jdbcType="VARCHAR" />
<result column="trans_tag" property="transTag" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
unit_id, device_id, device_type, end_time, operator_id, comment, operator_result,
trans_tag
</sql>
<insert id="insertLogOperation" parameterType="com.unisits.zngkpt.framework.logframe.pojo.LogOperation" >
insert into log_operation (unit_id, device_id, device_type,
end_time, operator_id, comment,
operator_result, trans_tag)
values (#{unitId,jdbcType=INTEGER}, #{deviceId,jdbcType=INTEGER}, #{deviceType,jdbcType=INTEGER},
#{endTime,jdbcType=TIMESTAMP}, #{operatorId,jdbcType=VARCHAR}, #{comment,jdbcType=VARCHAR},
#{operatorResult,jdbcType=VARCHAR}, #{transTag,jdbcType=INTEGER})
</insert>
</mapper>
日志DAO对应的mapper文件
package com.unisits.zngkpt.framework.logframe.service; import com.unisits.zngkpt.framework.logframe.pojo.LogOperation; /**
* @author:lyy
* @Date: 2017/6/25 12:03
* @version:
* @Description:
*/
public interface LogOperationService {
/**
* @author: lyy
* @Time: 2017/6/25 12:05
* @Descrption: 插入操作日志
* @param logOperation
* @return
* @throws
*/
public void InsertOperateLog(LogOperation logOperation);
}
日志Service接口
package com.unisits.zngkpt.framework.logframe.service; import com.unisits.zngkpt.framework.logframe.mapper.LogOperationDao;
import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @author:lyy
* @Date: 2017/6/25 12:03
* @version:
* @Description:
*/
@Service("logOperationService")
public class LogOperationServiceImpl implements LogOperationService{
@Autowired
LogOperationDao logOperationDao; /**
* @param logOperation
* @return
* @throws
* @author: lyy
* @Time: 2017/6/25 12:05
* @Descrption: 插入操作日志
*/
@Override
public void InsertOperateLog(LogOperation logOperation) {
logOperationDao.insertLogOperation(logOperation);
}
}
日志Service实现类
上面的操作数据库的基本完成了,那么下面就开始真正的切面Aspect了,因为我们要实现注解来实现AOP,所以,先自定义一个注解
第三步,定义一个注解
package com.unisits.zngkpt.framework.logframe.bojo; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @author:lyy
* @Date: 2017/6/25 12:17
* @version:
* @Description:
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OperAnnotation {
//模块名
String moduleName(); //操作内容
String option();
}
自定义注解
需要了解更多注解,请点击更多注解
第四步,重头戏来了,真正的Aspect
package com.unisits.zngkpt.framework.logframe.bojo; import com.unisits.zngkpt.framework.logframe.pojo.LogOperation;
import com.unisits.zngkpt.framework.logframe.service.LogOperationService;
import com.unisits.zngkpt.framework.privilegeframe.bojo.ActiveUser;
import org.apache.shiro.SecurityUtils;
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 org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import java.text.ParseException; /**
* @author:lyy
* @Date: 2017/6/25 12:07
* @version:
* @Description:
*/
@Component
@Aspect
public class LogAspect {
@Autowired
private LogOperationService logOperationService;//日志记录Service /**
* 方法切入点
*/
@Pointcut("execution(* com.unisits.zngkpt.common.*.controller.InforeleaseController.*(..))")
public void pointerCutMethod() {
} /**
* 后置通知
*
* @param jp
* 连接点
* @param annotation
* 返回值
*/
@AfterReturning(value = "pointerCutMethod() && @annotation(annotation)")
public void doAfter(JoinPoint jp, OperAnnotation annotation) throws ParseException{
// System.out.print(jp.getTarget().getClass() + "对象上被");
// System.out.print(jp.getSignature().getName() + "方法删除了");
//在这里可以区分不同的操作,比如登录,退出,普通操作等等
LogOperation log = new LogOperation();
//通过注解获取当前属于那个模块
log.setComment(annotation.moduleName()+":"+annotation.option());
//获取操作时间
log.setEndTime(DateUtils.getCurrentDate());
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
if (ra != null) {
ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
if (activeUser != null) {
log.setOperatorId(activeUser.getUserId());
log.setUnitId(Integer.valueOf(activeUser.getUnitId()));
log.setDeviceId(1);
log.setDeviceType(503);
}
}
try {
Object object = jp.getTarget();
if (object != null) {
log.setOperatorResult("成功");
}
logOperationService.InsertOperateLog(log);
} catch (Throwable e) {
log.setOperatorResult("失败:" + e.getMessage());
logOperationService.InsertOperateLog(log);
}
} }
Aspect
我这里使用了一个时间格式类,如下
package com.unisits.zngkpt.framework.logframe.bojo; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* @author:lyy
* @Date: 2017/6/25 10:42
* @version:
* @Description:
*/
@Component
public class DateUtils {
private static String defaultDatePattern = "yyyy-MM-dd HH:mm:ss.SSS"; /**
* 获得默认的 date pattern
*/
public static String getDatePattern()
{
return defaultDatePattern;
} /**
* 返回预设Format的当前日期字符串
*/
public static String getToday()
{
Date today = new Date();
return format(today);
} /**
* 使用预设Format格式化Date成字符串
*/
public static String format(Date date)
{
return date == null ? " " : format(date, getDatePattern());
} /**
* 使用参数Format格式化Date成字符串
*/
public static String format(Date date, String pattern)
{
return date == null ? " " : new SimpleDateFormat(pattern).format(date);
} /**
* 使用预设格式将字符串转为Date
*/
public static Date parse(String strDate) throws ParseException
{
return StringUtils.isBlank(strDate) ? null : parse(strDate,
getDatePattern());
} /**
* 使用参数Format将字符串转为Date
*/
public static Date parse(String strDate, String pattern)
throws ParseException
{
return StringUtils.isBlank(strDate) ? null : new SimpleDateFormat(
pattern).parse(strDate);
} /**
* 在日期上增加数个整月
*/
public static Date addMonth(Date date, int n)
{
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.MONTH, n);
return cal.getTime();
} public static String getLastDayOfMonth(String year, String month)
{
Calendar cal = Calendar.getInstance();
// 年
cal.set(Calendar.YEAR, Integer.parseInt(year));
// 月,因为Calendar里的月是从0开始,所以要-1
// cal.set(Calendar.MONTH, Integer.parseInt(month) - 1);
// 日,设为一号
cal.set(Calendar.DATE, 1);
// 月份加一,得到下个月的一号
cal.add(Calendar.MONTH, 1);
// 下一个月减一为本月最后一天
cal.add(Calendar.DATE, -1);
return String.valueOf(cal.get(Calendar.DAY_OF_MONTH));// 获得月末是几号
} public static Date getDate(String year, String month, String day)
throws ParseException
{
String result = year + "- "
+ (month.length() == 1 ? ("0 " + month) : month) + "- "
+ (day.length() == 1 ? ("0 " + day) : day);
return parse(result);
} public static Date getCurrentDate() throws ParseException{
String strNow = new SimpleDateFormat(getDatePattern()).format(new Date());
return new SimpleDateFormat(getDatePattern()).parse(strNow);
}
}
DateUtils
完成了,那么怎么告知spring容器我们有这个呢?那就是开启注解,该配置必须写在springmvc的配置文件,由于我们现在的方法切入点是在controller层,如果你定义在spring的配置文件里面,不会起作用。这牵扯到父子容器的问题。spring默认的主配置文件为父容器,springmvc为子容器,子容器可以使用父容器里面的配置信息,但是父容器却无法使用子容器的配置信息。
<!-- 加入Aspectj配置 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
好的,现在注解就开启了,那么怎么在控制器里调用呢?
如下
@OperAnnotation(moduleName = "情报板页面",option = "打开")
@RequestMapping("/infoman")
public String QueryInfoRelease(){
return CommonLib.INFORELEASE_URL_DIR+"inforelease";
}
好的,这里基于注解开发的完成了。
更所详细的如下:
1,自定义注解
3,本日志源码和jar包(因为这里有ActiveUser,自定义的类,主要用于shiro认证框架时保存的参数)
spring+mybatis基于 AOP实现业务日志管理的更多相关文章
- Spring MVC 中使用AOP 进行统一日志管理--XML配置实现
1.介绍 上一篇博客写了使用AOP进行统一日志管理的注解版实现,今天写一下使用XML配置实现版本,与上篇不同的是上次我们记录的Controller层日志,这次我们记录的是Service层的日志.使用的 ...
- Spring 中基于 AOP 的 @AspectJ
Spring 中基于 AOP 的 @AspectJ @AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格. 通过在你的基于架构的 XML ...
- Spring 中基于 AOP 的 XML架构
Spring 中基于 AOP 的 XML架构 为了使用 aop 命名空间标签,你需要导入 spring-aop j架构,如下所述: <?xml version="1.0" e ...
- Spring Boot AOP 简易操作日志管理
AOP (Aspect Oriented Programming) 面向切面编程. 业务有核心业务和边缘业务. 比如用户管理,菜单管理,权限管理,这些都属于核心业务. 比如日志管理,操作记录管理,这些 ...
- Spring MVC 中使用AOP 进行统一日志管理--注解实现
1.AOP简介 AOP称为面向切面编程 AOP的基本概念 (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知 (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的 ...
- Spring+Mybatis基于注解整合Redis
基于这段时间折腾redis遇到了各种问题,想着整理一下.本文主要介绍基于Spring+Mybatis以注解的形式整合Redis.废话少说,进入正题. 首先准备Redis,我下的是Windows版,下载 ...
- SpringMvc+Spring+MyBatis 基于注解整合
最近在给学生们讲Spring+Mybatis整合,根据有的学生反映还是基于注解实现整合便于理解,毕竟在先前的工作中团队里还没有人完全舍弃配置文件进行项目开发,由于这两个原因,我索性参考spring官方 ...
- Logstash+ Kafka基于AOP 实时同步日志到es
Logstash是一个开源数据收集引擎,具有实时管道功能.Logstash可以动态地将来自不同数据源的数据统一起来,并将数据标准化到你所选择的目的地,logstash丰富的插件(logstash-in ...
- Spring中基于AOP的XML架构
以下内容引用自http://wiki.jikexueyuan.com/project/spring/aop-with-spring-framenwork/xml-schema-based-aop-wi ...
随机推荐
- fuser 和 lsof
FUSER fuser功能fuser 可以显示出当前哪个程序在使用磁盘上的某个文件.挂载点.甚至网络端口,并给出程序进程的详细信息. fuser显示使用指定文件或者文件系统的进程ID.默认情况下每个文 ...
- 在Android中解决内存溢出 – OutOfMemoryError
原文链接:http://riggaroo.co.za/fixing-memory-leaks-in-android-outofmemoryerror/ 注:本文在原文基础上在如何判断内存是否泄露方面进 ...
- 设计模式之外观模式(PHP实现)
github地址:https://github.com/ZQCard/design_pattern/** * 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访 ...
- lamp+nginx代理+discuz+wordpress+phpmyadmin
实验课题:搭建LAMP,安装Nginx,作为代理,将MySQL安装在单独的机器,apache负责动态,nginx负责静态 实验环境: 1.VMware Workstation 11 2.设备A:MyS ...
- 控制流程完整性:给大家介绍一种“另类”的Javascript反分析技术
写在前面的话 理解恶意软件的真实代码对恶意软件分析人员来说是非常有优势的,因为这样才能够真正了解恶意软件所要做的事情.但不幸的是,我们并不总是能够得到“真实”的代码,有时恶意软件分析人员可能需要类似反 ...
- ES6中的Map集合(与java里类似)
Set类型可以用来处理列表中的值,但是不适用于处理键值对这样的信息结构.ES6也添加了Map集合来解决类似的问题 一.Map集合 JS的对象(Object),本质上是键值对的集合(Hash结构),但是 ...
- 2017.3.31 spring mvc教程(一)核心类与接口
学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...
- jsp中URL传递中文參数的处理
在页面的url中使用encodeURI(encodeURI(中文)).对中文进行编码.并在server的java程序中使用URLDecoder.decode(中文, "UTF-8" ...
- hdu 3667 /2010哈尔滨赛区H题 费用与流量为非线性关系/费用流
题意: 在一般费用流题目改动:路过某路,每x单位流量须要花费 ai*x^2(ai为给定的系数). 開始的的时候,一看仅仅只是是最后统计费用上在改动罢了,一看例子.发现根本没那么简单(ps:以后每次写程 ...
- 【VBA】VBA编写的,将一列中相同的内容的行提取出来单独生成文件
数据如上图所示,点击RUN后的运行结果如下: 得到该文件夹,文件夹内容如上图. 代码如下: Private Sub Command_OLIVER() Dim arr arr = Range(" ...