您的位置:首页 > 编程语言 > Java开发

Spring AOP 实现业务和异常日志记录实战

2018-09-19 15:04 1006 查看

1 业务需求:今日,公司要求对操作的业务和日志统一做处理,需要把业务表数据相关信息存入日志表中,比如表名,方法名,业务id,操作操作时间modifyTIme等等。

除了在业务主动插入日志数据之外,有个比较好的方法就是用面向切面aop处理,明确跟业务逻辑分开,把业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

2 业务开发,这边处理的方式是用方式1【两种方式 1 利用注解方式 2 通过xml配置】

2.1 定义切入点接口类

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 "";

}


2.2 定义日志记录切面类

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方式,拦解注解的日志
* 后置通知:如果需要访问其他建议类型的连接点上下文,则应使用JoinPoint参数类型而不是ProceedingJoinPoint。
*/
@Pointcut(value = "@annotation(com.hec.dup.facade.mgr.annotation.OperateLogAnnotation)")
public void logAspect() {
}

@After("logAspect()")
public Object recordLog(JoinPoint point) throws Throwable {
//先执行业务
Object result = point.proceed();
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());
}
}
}*/

Str
5ac
ingBuilder 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();  //获取为null
logmsg = 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( Operat
3900
eLogAnnotation.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() );//用户ID
operaLog.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() ); //操作表名

JSONArray paramArray = JSONObject.parseArray( params.toString() ); //获取第一个参数的id
operaLog.setTableId( paramArray.getJSONObject( 0 ).getString( "id" ) );

operaLog.setOperateContent( annotation.operateContent() ); //操作内容
return operaLog;
}

}


2.3 定义applicationContext.xml配置



2.4 接口添加注解,接口被调用的时候会调用被aop处理

@OperateLogAnnotation(moduleType = "01")  //这边添加切入点接口的注解
@RequestMapping(value = "testAop01")
@ResponseBody
public 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 的执行顺序

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring mvc aop Aspect