SpringAOP之日志管理

来源:互联网 发布:java快速开发平台比较 编辑:程序博客网 时间:2024/05/18 01:58

所谓AOP就是面向切面编程,简单的说就是在原来的方法基础上通过AOP切面对该方法进行增强。

一、Spring默认不支持@AspectJ风格的切面声明,为了支持需要在配置文件中添加如下配置:

<aop:aspectj-autoproxy/>

如下所示:

注:以上配置如果在controller层和service层中都用到,那么都需要在controller的配置和spring主配置都添加,如果不添加的效果在下面会有讲解。

二、定义切面aspect

1、定义切入面:在所定义的类中使用注解@Aspect即表示该类作为切面,如:

这里写图片描述

2、定义切入点:在这里我使用的是注解的方式(也可以使用较常用的通配符方式)

这里写图片描述

2.1 定义注解:

这里写图片描述

3、定义通知:根据业务需求自己定义,在这里我使用的是后置通知:即表示的是在使用该注解@LogController方法执行完之后再执行此切面方法

这里写图片描述

getControllerMethodDescription()

/**     * 获取注解中对方法的描述信息 用于Controller层注解     *      * @param joinPoint 切入点     * @return 方法描述     * @throws Exception     */    @SuppressWarnings("rawtypes")    private Map<String, String> getControllerMethodDescription(JoinPoint joinPoint) throws Exception {        Map<String, String> result = new HashMap<String, String>();        //类名        String targetName = joinPoint.getTarget().getClass().getName();        //方法名称        String methodName = joinPoint.getSignature().getName();        //获取入参列表        Object[] arguments = joinPoint.getArgs();        //类对象        Class<?> targetClass = Class.forName(targetName);        Method[] methods = targetClass.getMethods();        for (Method method : methods) {            if (method.getName().equals(methodName)) {                Class[] clazzs = method.getParameterTypes();                if (clazzs.length == arguments.length) {                    //获取注解对象                    LogController logController=method.getAnnotation(LogController.class);                    result.put("logType", logController.logType().toString());                    result.put("operation", logController.operation());                    result.put("description", targetName+joinPointStr+methodName+leftJoinStr+getParamsRemark(joinPoint,logController, arguments)+rightJoinStr);                    break;                }            }        }        return result;    }

getParamsRemark()

private String getParamsRemark(JoinPoint joinPoint,LogController logController, Object[] param){        System.out.println(logController.propertys()                +"==========");        //如果没传递参数属性,说明传递的是对象        if(StringUtils.isBlank(logController.propertys())){            return getMethodParams(joinPoint);        }        if(param ==null || param.length==0){            param[0]="";        }        //获取参数属性  name,age        String propertys = logController.propertys();        StringBuffer sb = new StringBuffer();          if(propertys == null || "".equals(propertys)){            return sb.toString();        }        //分割获取参数属性名称    name  age        String[] propertysRemark = propertys.split(splitParaStr);          if(propertysRemark == null || propertysRemark.length == 0){            return sb.toString();        }        int count = 0;        for (int i=0;i<propertysRemark.length;i++) {            Object name=null;            if(param[0] instanceof String){                //表明是多个参数,否则就是对象                name=param[i];            }else{                name=param[0];            }            //参数属性对应的值            String value =  propertysRemark[i];            if(count == propertysRemark.length){                sb.append(propertysRemark[i] + splitNameValueStr + getID(name, value) +splitParaStr);              }else{                sb.append(propertysRemark[i] + splitNameValueStr + getID(name, value));            }            count++;            sb.append(splitParaStr);        }        return sb.substring(0, sb.length()-1).toString();    }  

getID()

 /**      * 通过java反射来从传入的参数object里取出我们需要记录的id,name等属性,        * 此处我取出的是id      */       private String getID(Object obj,String param){           if(obj instanceof String){               return obj.toString();           }        if(obj instanceof Integer){               return obj.toString();           }       //构造set和get        PropertyDescriptor pd = null;           Method method = null;           String v = "";           try{               pd = new PropertyDescriptor(param,obj.getClass());               method = pd.getReadMethod();                 v = String.valueOf(method.invoke(obj));            }catch (Exception e) {               e.printStackTrace();           }        return v;       }

getMethodParams()

/**     *      * @描述:获取方法参数     * @创建人:rz.li     * @创建时间:2017年7月18日上午9:36:04     * @param joinPoint     * @return     */    private String getMethodParams(JoinPoint joinPoint) {        String params = "";        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {            for (int i = 0; i < joinPoint.getArgs().length; i++) {                Object object = joinPoint.getArgs()[i];                try {                    params += JSONUtils.obj2json(object) + ";";                } catch (Exception e) {                    e.printStackTrace();                }            }        }        return params;    }

4、开始使用切面:

在Controller层中

这里写图片描述

5、测试结果:

这里写图片描述

三、异常信息的处理

1、定义:对于异常信息的AOP,跟上面定义的方式一样,同样使用注解方式,代码如下:

    @Pointcut("@annotation(com.demo.annotation.LogService)")    public void serviceAspect() {}

2、注解:

@Target({ElementType.PARAMETER, ElementType.METHOD})  @Retention(RetentionPolicy.RUNTIME)  @Documented  public @interface LogService {    /** 要执行的操作类型比如:add操作 **/      public LogTypeEnum logType() default LogTypeEnum.SYS_LOG;     /** 要执行的具体操作比如:添加用户 **/      public String description() default "";}

3、使用:

    使用后置异常通知进行对Service层异常信息的捕获,此时在service方法中不需要try-catch捕获异常信息,如下所示:
    @LogService(logType=LogTypeEnum.SYS_LOG,description="测试aop异常捕获")    @Override    public void testAop(BatchSellPO b) {        int i=1/0;        System.out.println(i);    }

具体异常处理逻辑

@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {        try {            //TODO 获取用户的信息            String userId="123";            System.out.println("=====异常通知开始=====");            System.out.println("异常代码:" + e.getClass().getName());            System.out.println("异常信息:" + e.getMessage());            System.out.println("异常方法:" + joinPoint.getTarget().getClass().getName()+joinPointStr+joinPoint.getSignature().getName() + "()");            System.out.println("方法描述:" + getServiceMethodDescription(joinPoint));            System.out.println("请求人ID:" + userId);            System.out.println("请求IP:" + request.getRemoteAddr());            System.out.println("请求参数:" + getMethodParams(joinPoint));            /*==========数据库日志=========*/              SysLog log = new SysLog();            //保存数据库              //sysLogService.insert(log);              System.out.println("=====异常通知结束=====");          } catch (Exception e2) {            logger.error(e2.getMessage(), e2);        }    }

getServiceMethodDescription()

/**     * @描述:获取注解中对方法的描述信息 用于Service层注解     * @创建人:rz.li     * @创建时间:2017年8月31日上午10:26:16     * @param joinPoint     * @return 方法描述     * @throws Exception     */    @SuppressWarnings("rawtypes")    private Map<String, Object> getServiceMethodDescription(JoinPoint joinPoint) throws Exception {        Map<String, Object> result = new HashMap<String, Object>();        String targetName = joinPoint.getTarget().getClass().getName();        String methodName = joinPoint.getSignature().getName();        Object[] arguments = joinPoint.getArgs();        Class<?> targetClass = Class.forName(targetName);        Method[] methods = targetClass.getMethods();        for (Method method : methods) {            if (method.getName().equals(methodName)) {                Class[] clazzs = method.getParameterTypes();                if (clazzs.length == arguments.length) {                    result.put("logType", method.getAnnotation(LogService.class).logType());                    result.put("description", method.getAnnotation(LogService.class).description());                    break;                }            }        }        return result;    }

4、测试
在访问的时候没进入代理方法,看异常信息发现默认使用的是jdk代理(接口代理),而AOP切面的注解时基于类的代理,故改为cglib代理(类代理),由此可判断在spring主配置文件中没有添加

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

这里写图片描述

添加配置之后测试结果如下:

这里写图片描述

说明异常切面已成功!

阅读全文
0 0