最近在項目上用到了操做日誌的相關,以前的解決方案就是本身寫一個日誌project,而後統一調用日誌接口便可,這樣方便自定義定製,由於有不少設備控制之類的都是須要確認一下的,可是,對數據的操做,好比,增刪改查還用這個,就有點多餘了,感受太麻煩了,因此,想一想有沒有日誌管理這樣的框架能夠調用,而後就想到了spring的AOP。html
整個項目的結構以下java
第一步,定義日誌類web
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,因此,這裏上面的操做類繼承了其主鍵類,spring
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和日誌的Servicesql
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); }
<?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>
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); }
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); } }
上面的操做數據庫的基本完成了,那麼下面就開始真正的切面Aspect了,由於咱們要實現註解來實現AOP,因此,先自定義一個註解數據庫
第三步,定義一個註解apache
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(); }
須要瞭解更多註解,請點擊更多註解mybatis
第四步,重頭戲來了,真正的Aspectmvc
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); } } }
我這裏使用了一個時間格式類,以下app
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); } }
完成了,那麼怎麼告知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認證框架時保存的參數)