生活随笔
收集整理的這篇文章主要介紹了
Spring AOP 实现业务和异常日志记录实战
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1 業(yè)務(wù)需求:今日,公司要求對(duì)操作的業(yè)務(wù)和日志統(tǒng)一做處理,需要把業(yè)務(wù)表數(shù)據(jù)相關(guān)信息存入日志表中,比如表名,方法名,業(yè)務(wù)id,操作操作時(shí)間modifyTIme等等。
除了在業(yè)務(wù)主動(dòng)插入日志數(shù)據(jù)之外,有個(gè)比較好的方法就是用面向切面aop處理,明確跟業(yè)務(wù)邏輯分開(kāi),把業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊之間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
2 業(yè)務(wù)開(kāi)發(fā),這邊處理的方式是用方式1【兩種方式 1 利用注解方式 2 通過(guò)xml配置】
package com.hec.dup.facade.mgr.annotation;import java.lang.annotation.*;/*** 標(biāo)記需要做業(yè)務(wù)日志的方法*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OperateLogAnnotation {/*** 被修改的實(shí)體的唯一標(biāo)識(shí),例如:菜單實(shí)體的唯一標(biāo)識(shí)為"id"*/String key() default "id";/*** 業(yè)務(wù)模塊類(lèi)型,例如:"01菜單管理"*/String moduleType() default "";/*** 業(yè)務(wù)模塊名稱,例如:"菜單管理"*/String moduleName() default "";/*** 業(yè)務(wù)模塊功能名稱,例如:"新增菜單功能"*/String functionName() default "";/*** 操作日志內(nèi)容,例如:"修改菜單"*/String operateContent() default "";/*** 業(yè)務(wù)模塊表名,例如:"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;/*** 通過(guò)AOP方式,攔解注解的日志 */@Pointcut(value = "@annotation(com.hec.dup.facade.mgr.annotation.OperateLogAnnotation)")public void logAspect() {}/*** 后置通知:如果需要訪問(wèn)其他建議類(lèi)型的連接點(diǎn)上下文,則應(yīng)使用JoinPoint參數(shù)類(lèi)型而不是ProceedingJoinPoint。*/@After("logAspect()")public Object recordLog(JoinPoint point) throws Throwable { try {this.handle(point);} catch (Exception e) {e.printStackTrace();log.error("日志記錄出錯(cuò)!", e);}return result;}/*** 獲取攔截方法參數(shù),處理日志* @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());//如果當(dāng)前用戶未登錄,不做日志PrjCurrUserInfo userInfo = (PrjCurrUserInfo)SecurityUtils.getSubject().getPrincipal();if (null == userInfo) {return;}//獲取攔截方法的參數(shù),獲取登錄用戶對(duì)象PrjUserEntity userEntity = userInfo.getUserEntity();/**// 攔截的實(shí)體類(lèi)Object target = joinPoint.getTarget();// 攔截的方法名稱String methodName = joinPoint.getSignature().getName();// 攔截的方法參數(shù)Object[] args = joinPoint.getArgs();// 攔截的參數(shù)類(lèi)型Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();**///獲取注解日志內(nèi)容OperateLogAnnotation annotation = method.getAnnotation(OperateLogAnnotation.class);//字典,替換成用table查詢表注釋//Class dictClass = annotation.dict();//通過(guò)表名,動(dòng)態(tài)獲取表字段名和注釋/* 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(" & ");}//如果涉及到修改,比對(duì)修改前后值的變化內(nèi)容String logmsg ="";/*if (methodName.indexOf("update") != -1 || methodName.indexOf("modify") != -1|| methodName.indexOf("edit") != -1) {Map<String, String> newMap = HttpKit.getRequestParameters(); //對(duì)象被修改后的內(nèi)容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);}/*** 封裝操作日志實(shí)體對(duì)象** @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(); //類(lèi)名稱,含路徑String classMethod = method.getName(); //方法名(英文)OperateLogAnnotation annotation = method.getAnnotation( OperateLogAnnotation.class );DupComOperateLogEntity operaLog = new DupComOperateLogEntity();operaLog.setId( UuidUtil.getUuid() ); //主鍵operaLog.setClassPath( classPath ); //類(lèi)名稱,含路徑operaLog.setClassMethod( classMethod ); //方法名(英文)operaLog.setIpHost( userEntity.getLastLoginIp() ); //IP地址operaLog.setOperateTime( new Date() ); //操作時(shí)間operaLog.setOperateUserId( userEntity.getId() );//用戶IDoperaLog.setOperateUserName( userEntity.getUserName() ); //用戶名稱operaLog.setUserAccount( userEntity.getAccount() ); //用戶帳號(hào)operaLog.setUserType( userEntity.getUserType() ); //帳號(hào)類(lèi)型operaLog.setModuleType( annotation.moduleType() );//業(yè)務(wù)模塊類(lèi)型operaLog.setModuleName( annotation.moduleName() );//業(yè)務(wù)模塊名稱operaLog.setFunctionName( annotation.functionName() );//業(yè)務(wù)模塊名稱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() ); //操作內(nèi)容return operaLog;}/*** 實(shí)體對(duì)象轉(zhuǎn)成Map* @param obj 實(shí)體對(duì)象* @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") //這邊添加切入點(diǎn)接口的注解@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 注意事項(xiàng):該方式主要是通過(guò)注解的方式,個(gè)人覺(jué)得比較便利,當(dāng)然也可以通過(guò)另外一種方式xml,比如 AOP實(shí)現(xiàn)方式3——通過(guò)<aop:config>來(lái)配置 ,需要注意的是aop的執(zhí)行順序,可參考Spring AOP @Before @Around @After 等 advice 的執(zhí)行順序
轉(zhuǎn)載于:https://blog.51cto.com/12066352/2177101
總結(jié)
以上是生活随笔為你收集整理的Spring AOP 实现业务和异常日志记录实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。