生活随笔
收集整理的這篇文章主要介紹了
Spring AOP 实现业务和异常日志记录实战
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1 業務需求:今日,公司要求對操作的業務和日志統一做處理,需要把業務表數據相關信息存入日志表中,比如表名,方法名,業務id,操作操作時間modifyTIme等等。
除了在業務主動插入日志數據之外,有個比較好的方法就是用面向切面aop處理,明確跟業務邏輯分開,把業務模塊所共同調用的邏輯或責任封裝起來,便于減少系統的重復代碼,降低模塊之間的耦合度,并有利于未來的可操作性和可維護性。
2 業務開發,這邊處理的方式是用方式1【兩種方式 1 利用注解方式 2 通過xml配置】
package com.hec.dup.facade.mgr.annotation;import java.lang.annotation.*;/*** 標記需要做業務日志的方法*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OperateLogAnnotation {/*** 被修改的實體的唯一標識,例如:菜單實體的唯一標識為"id"*/String key() default "id";/*** 業務模塊類型,例如:"01菜單管理"*/String moduleType() default "";/*** 業務模塊名稱,例如:"菜單管理"*/String moduleName() default "";/*** 業務模塊功能名稱,例如:"新增菜單功能"*/String functionName() default "";/*** 操作日志內容,例如:"修改菜單"*/String operateContent() default "";/*** 業務模塊表名,例如:"SYS_MENU"*/String tableName() default "";}
package com.hec.dup.facade.mgr.aspect;import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hec.dup.beans.base.PrjUserEntity;
import com.hec.dup.beans.base.vo.PrjCurrUserInfo;
import com.hec.dup.beans.com.DupComOperateLogEntity;
import com.hec.dup.common.utils.UuidUtil;
import com.hec.dup.facade.base.IPrjUserService;
import com.hec.dup.facade.com.IDupComOperateLogService;
import com.hec.dup.facade.mgr.annotation.OperateLogAnnotation;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Date;/*** 日志記錄切面* @author*/
@Aspect
@Component
public class OperateLogAspect {private Logger log = LoggerFactory.getLogger(this.getClass());@Autowired(required = false)private IDupComOperateLogService operateLogService;@Autowired(required = false)private IPrjUserService prjUserService;/*** 通過AOP方式,攔解注解的日志 */@Pointcut(value = "@annotation(com.hec.dup.facade.mgr.annotation.OperateLogAnnotation)")public void logAspect() {}/*** 后置通知:如果需要訪問其他建議類型的連接點上下文,則應使用JoinPoint參數類型而不是ProceedingJoinPoint。*/@After("logAspect()")public Object recordLog(JoinPoint point) throws Throwable { try {this.handle(point);} catch (Exception e) {e.printStackTrace();log.error("日志記錄出錯!", e);}return result;}/*** 獲取攔截方法參數,處理日志* @param point* @throws Exception*/private void handle(ProceedingJoinPoint point) throws Exception {//獲取攔截的方法名Signature sig = point.getSignature();MethodSignature msig = null;if (!(sig instanceof MethodSignature)) {throw new IllegalArgumentException("該注解只能用于方法");}msig = (MethodSignature) sig;Object[] params = point.getArgs();Object target = point.getTarget();Method method = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());//如果當前用戶未登錄,不做日志PrjCurrUserInfo userInfo = (PrjCurrUserInfo)SecurityUtils.getSubject().getPrincipal();if (null == userInfo) {return;}//獲取攔截方法的參數,獲取登錄用戶對象PrjUserEntity userEntity = userInfo.getUserEntity();/**// 攔截的實體類Object target = joinPoint.getTarget();// 攔截的方法名稱String methodName = joinPoint.getSignature().getName();// 攔截的方法參數Object[] args = joinPoint.getArgs();// 攔截的參數類型Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();**///獲取注解日志內容OperateLogAnnotation annotation = method.getAnnotation(OperateLogAnnotation.class);//字典,替換成用table查詢表注釋//Class dictClass = annotation.dict();//通過表名,動態獲取表字段名和注釋/* Map<String,String> metaMap = new HashMap<>();//MetaTableVo metaTableVo = metaTableService.getByTableName(table);if(StringUtil.isNotEmpty(table)){List<MetaColumVo> metaColumns = metaColumService.queryByTable(table);if(metaColumns!=null && metaColumns.size()>0){for(MetaColumVo vo:metaColumns){metaMap.put(vo.getColumnName(),vo.getColumnAlias());}}}*/StringBuilder sb = new StringBuilder();for (Object param : params) {sb.append(param);sb.append(" & ");}//如果涉及到修改,比對修改前后值的變化內容String logmsg ="";/*if (methodName.indexOf("update") != -1 || methodName.indexOf("modify") != -1|| methodName.indexOf("edit") != -1) {Map<String, String> newMap = HttpKit.getRequestParameters(); //對象被修改后的內容Object oldObj = redisSupport.getObject(HttpKit.getRequest().getSession().getId());//Object oldObj = LogObjectHolder.me().get(); //獲取為nulllogmsg = ContrastObjFactory.contrastObj(table,key,metaMap,newMap,oldObj);} else {logmsg=content;}*///記錄操作日志DupComOperateLogEntity operateLogEntity = getOperaLog( userEntity, target, method, logmsg, params );operateLogService.save(operateLogEntity);}/*** 封裝操作日志實體對象** @Date 2017/3/30 18:45*/public static DupComOperateLogEntity getOperaLog(PrjUserEntity userEntity, Object target, Method method, String msg, Object[] params) {String classPath = target.getClass().getName(); //類名稱,含路徑String classMethod = method.getName(); //方法名(英文)OperateLogAnnotation annotation = method.getAnnotation( OperateLogAnnotation.class );DupComOperateLogEntity operaLog = new DupComOperateLogEntity();operaLog.setId( UuidUtil.getUuid() ); //主鍵operaLog.setClassPath( classPath ); //類名稱,含路徑operaLog.setClassMethod( classMethod ); //方法名(英文)operaLog.setIpHost( userEntity.getLastLoginIp() ); //IP地址operaLog.setOperateTime( new Date() ); //操作時間operaLog.setOperateUserId( userEntity.getId() );//用戶IDoperaLog.setOperateUserName( userEntity.getUserName() ); //用戶名稱operaLog.setUserAccount( userEntity.getAccount() ); //用戶帳號operaLog.setUserType( userEntity.getUserType() ); //帳號類型operaLog.setModuleType( annotation.moduleType() );//業務模塊類型operaLog.setModuleName( annotation.moduleName() );//業務模塊名稱operaLog.setFunctionName( annotation.functionName() );//業務模塊名稱operaLog.setTableName( annotation.tableName() ); //操作表名if (params != null && params.length > 0) {Map<String, Object> paramMap = object2Map( params[0] );operaLog.setTableId( paramMap.get( "id" ).toString() );}operaLog.setOperateContent( annotation.operateContent() ); //操作內容return operaLog;}/*** 實體對象轉成Map* @param obj 實體對象* @return*/public static Map<String, Object> object2Map(Object obj) {Map<String, Object> map = new HashMap<>();if (obj == null) {return map;}Class clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();try {for (Field field : fields) {field.setAccessible(true);map.put(field.getName(), field.get(obj));}} catch (Exception e) {e.printStackTrace();}return map;}}
@OperateLogAnnotation(moduleType = "01") //這邊添加切入點接口的注解@RequestMapping(value = "testAop01")@ResponseBodypublic JsonResult testAop(BaseEntity baseEntity) {JsonResult jr = new JsonResult();baseEntity.setCreateTime( new Date() );baseEntity.setDbUser( "123456789," );baseEntity.setExport( true );jr.setData( baseEntity );return jr;}
3 注意事項:該方式主要是通過注解的方式,個人覺得比較便利,當然也可以通過另外一種方式xml,比如 AOP實現方式3——通過<aop:config>來配置 ,需要注意的是aop的執行順序,可參考Spring AOP @Before @Around @After 等 advice 的執行順序
轉載于:https://blog.51cto.com/12066352/2177101
總結
以上是生活随笔為你收集整理的Spring AOP 实现业务和异常日志记录实战的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。