利用Spring AOP统一处理日志和异常

it2022-05-05  111

第一步:

1. 增加spring AOP相关配置到spring-context.xml:

<!-- 启动对@AspectJ注解的支持 --> <!-- proxy-target-class等于true是强制使用cglib代理,proxy-target-class默认是false,如果你的类实现了接口就走JDK代理,如果没有,走cglib代理 --> <!-- 注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib --> <!--如果不写proxy-target-class="true"这句话也没问题 -->

<aop:aspectj-autoproxy proxy-target-class="true" />

2. pom.xml增加相关依赖:

<!-- AOP -->         <dependency>             <groupId>org.aspectj</groupId>             <artifactId>aspectjrt</artifactId>             <version>1.6.11</version>         </dependency>         <dependency>             <groupId>org.aspectj</groupId>             <artifactId>aspectjweaver</artifactId>             <version>1.6.11</version>         </dependency>         <dependency>             <groupId>cglib</groupId>             <artifactId>cglib</artifactId>             <version>2.1_3</version>         </dependency>         <dependency>             <groupId>javassist</groupId>             <artifactId>javassist</artifactId>             <version>3.12.1.GA</version>         </dependency>

第二布:

创建相关自定义注解类,InterfaceParam.java 和 JzInterface.java

package com.abc.annotation;   import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;   /**  * 自定义注解类(方法描述)  *   * @author DaiHaijiao  *  */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface JzInterface {       /**      * 方法描述:接口名称      *       * @return String      */     String value() default "";       /**      * 方法描述:参数列表      *       * @return InterfaceParam[]      */     InterfaceParam[] params() default {}; }

package com.abc.annotation;   import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;   /**  * 自定义注解类(接口描述信息)  *   * @author DaiHaijiao  *  */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface InterfaceParam {       /**      * 方法描述:参数名称      *       * @return String      */     String name() default "";       /**      * 方法描述:参数说明      *       * @return String      */     String desc() default "";   }

第三步:

创建操作日志实体对象,属性字段如下:

// 请求状态     public static final int STATUS_FAIL = 0; // 失败     public static final int STATUS_SUCCESS = 1; // 成功       private ObjectId _id;     private String userId; // 用户ID     private String integerfaceDesc; // 接口描述     private String receptionParams; // 前台参数     private String backstageParams; // 后台参数     private int status; // 请求状态     private String menuKey; // 菜单key     private Boolean deleted; // 删除标记     private Date createdAt; // 创建时间     private Date updatedAt; // 更新时间

第四部:

编写AOP拦截类:

package com.abc.aop;   import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;   import javax.servlet.http.HttpServletRequest;   import org.apache.shiro.SecurityUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterThrowing; 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 org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;   import com.abc.annotation.InterfaceParam; import com.abc.annotation.JzInterface; import com.abc.entity.OperationLog; import com.abc.entity.User; import com.abc.service.OperationLogService;   import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.CodeAttribute; import javassist.bytecode.LocalVariableAttribute; import javassist.bytecode.MethodInfo;   /**  * 采用AOP的方式处理日志和异常问题  *   */ @Aspect @Component public class BindAop {       @Autowired     private OperationLogService operationLogService;       private final Logger log = LoggerFactory.getLogger(this.getClass());       // 接口描述     private String interfaceDescribe = "";       // 接口参数     InterfaceParam[] param = null;       // 接口详细参数字符串map     Map<String, String> parameContentMap = null;       OperationLog operationLog = null;       @Pointcut("execution(* com.abc.controller.*.*(..))")     public void aopMethod() {     }       /**      * 环绕通知      *       * @param joinPoint      * @return Object      * @throws Throwable      */     @Around("aopMethod()")     public Object around(ProceedingJoinPoint joinPoint) throws Throwable {         log.info("before method invoking!");         Signature signature = joinPoint.getSignature();         MethodSignature methodSignature = (MethodSignature) signature;         Method method = methodSignature.getMethod();         JzInterface jzInterface = method.getAnnotation(JzInterface.class);         if (null != jzInterface) {             String classType = joinPoint.getTarget().getClass().getName();// 接口所在的包名类名             // Class<?> clazz = Class.forName(classType);             // String clazzName = clazz.getName();// 接口所在的类名             // String clazzSimpleName = clazz.getSimpleName();// 接口所在的类名             String methodName = joinPoint.getSignature().getName();// 被请求的接口名             String[] paramNames = getFieldsName(this.getClass(), classType, methodName);// 被请求的接口的参数的key             parameContentMap = appendParameInfo(paramNames, joinPoint, jzInterface.params());             interfaceDescribe = jzInterface.value();         } else {             interfaceDescribe = "";         }         return joinPoint.proceed();     }       /**      * 后置通知      *      *       * @param joinPoint      */     @After("aopMethod()")     public void after(JoinPoint joinPoint) {         if (!"".equals(interfaceDescribe)) {// 获取接口的描述             String parame = parameContentMap.get("parame");             String parameInfo = parameContentMap.get("parameInfo");             // 接口访问成功(加入日志操作记录)             operationLog = new OperationLog();             // operationLog对象set相关属性值             operationLog.setIntegerfaceDesc(interfaceDescribe);             operationLog.setReceptionParams(parameInfo);             operationLog.setBackstageParams(parame);             operationLog.setStatus(OperationLog.STATUS_SUCCESS);// 成功             operationLog.setUserId(((User) SecurityUtils.getSubject().getPrincipal()).get_id() + "");//此处用户id根据自己项目实际情况获取             operationLog.setDeleted(false);             operationLog.setMenuKey("-");             // 操作日志入库             operationLog = operationLogService.add(operationLog);         }     }       /**      * 异常通知      *       * @param joinPoint      * @param e      */     @AfterThrowing(pointcut = "aopMethod()", throwing = "e")     public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();         String url = request.getRequestURI();         if (!"".equals(interfaceDescribe)) {             String parame = parameContentMap.get("parame");             String parameInfo = parameContentMap.get("parameInfo");             // 接口访问失败(修改日志操作记录)             operationLog.setIntegerfaceDesc(interfaceDescribe);             operationLog.setReceptionParams(parameInfo);             operationLog.setBackstageParams(parame);             operationLog.setStatus(OperationLog.STATUS_FAIL);// 失败             operationLog.setUserId(((User) SecurityUtils.getSubject().getPrincipal()).get_id() + "");             operationLog.setDeleted(false);             operationLog.setMenuKey("-");             // 修改刚才插入数据库的数据             operationLogService.edit(operationLog);         }         // 错误信息         log.error("访问" + url + " 发生错误, 错误信息:" + e.getMessage());         // TODO ... 错误信息保存数据库 ...     }       /**      * 拼接参数key以及参数value      *       * @param paramNames      * @param joinPoint      * @param param      * @return String      */     private Map<String, String> appendParameInfo(String[] paramNames, JoinPoint joinPoint, InterfaceParam[] param) {         Map<String, String> map = new HashMap<String, String>();         Object[] args = joinPoint.getArgs();         StringBuilder sb = new StringBuilder();         StringBuilder sb1 = new StringBuilder();         sb.append("{");         sb1.append("{");         int length = args.length;         if (param.length == length) {             int lastArgsNum = args.length - 1;             for (int k = 0; k < length; k++) {                 Object arg = args[k];                 sb.append("\"" + paramNames[k] + "\"");                 sb1.append("\"" + param[k].desc() + "\"");                 if (k == lastArgsNum) {                     sb.append(":\"" + arg + "\"");                     sb1.append(":\"" + arg + "\"");                 } else {                     sb.append(":\"" + arg + "\",");                     sb1.append(":\"" + arg + "\",");                 }             }         }         sb.append("}");         sb1.append("}");         map.put("parame", sb.toString());         map.put("parameInfo", sb1.toString());         return map;     }       /**      * 得到方法参数的名称      *       * @param cls      * @param clazzName      * @param methodName      * @return String[]      * @throws NotFoundException      */     @SuppressWarnings("rawtypes")     private String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException {         ClassPool pool = ClassPool.getDefault();         ClassClassPath classPath = new ClassClassPath(cls);         pool.insertClassPath(classPath);           CtClass cc = pool.get(clazzName);         CtMethod cm = cc.getDeclaredMethod(methodName);         MethodInfo methodInfo = cm.getMethodInfo();         CodeAttribute codeAttribute = methodInfo.getCodeAttribute();         LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);         if (attr == null) {             // exception         }         String[] paramNames = new String[cm.getParameterTypes().length];         int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;         for (int i = 0; i < paramNames.length; i++) {             paramNames[i] = attr.variableName(i + pos); // paramNames即参数名         }         return paramNames;     }   }

第五步:

使用:

1. eg,接口需要将调用日志添加到数据库,则如下:

/**      * 新建角色      *      * @param name      * @param desc      * @param menuKeys      * @return Result      */     @JzInterface(value = "新建角色", params = { @InterfaceParam(name = "name", desc = "角色名称"), @InterfaceParam(name = "desc", desc = "备注"),             @InterfaceParam(name = "menuKeys", desc = "角色权限") })     @RequestMapping(value = "add.do")     public @ResponseBody Result add(String name, String desc, String menuKeys) {         //TODO ...                 // ...                 // ...         return null;     } 2. eg,接口不需要将调用日志添加到数据库,则如下: /**      * 新建角色      *      * @param name      * @param desc      * @param menuKeys      * @return Result      */     @RequestMapping(value = "add.do")     public @ResponseBody Result add(String name, String desc, String menuKeys) {         //TODO ...                 // ...                 // ...         return null;     } 3. 说明,当需要添加日志,但是接口中又没有参数时,注解中的 params 直接 = {} 即可。当接口参数中包含有request、response、session或其他对象时,注解中的params也同样需要写入该参数,eg:

@JzInterface(value = "获取人员数据列表", params = { @InterfaceParam(name = "page", desc = "页码"), @InterfaceParam(name = "limit", desc = "条数"),             @InterfaceParam(name = "temp", desc = "0:初始化页面,不是0说明不是初始化页面"), @InterfaceParam(name = "session", desc = "HttpSession对象") })     @RequestMapping(value = "IMOS_page_list.do")     public @ResponseBody Map<String, Object> person_page_list(Integer page, Integer limit, String temp, HttpSession session) {         Map<String, Object> map = new HashMap<String, Object>();         //TODO ...                 // ... ...                 // ... ...         return map;     }

最后一点,要注意:params 里面的参数顺序必须和接口参数顺序保持一致!!! ---------------------  原文:https://blog.csdn.net/Dai_Haijiao/article/details/80365753   


最新回复(0)