zoukankan      html  css  js  c++  java
  • 用切面对监控日志的实现2

    一,关于切面的概念

    1 五种通知类型

    @Before 前置通知   可以拿到请求参数

    @Around 环绕通知  可以拿到请求参数,返回值,及控制方法是否执行,还能抓取异常

    @AfterReturning 后置通知  可以拿到请求参数,返回值

    @After 最终通知  类似finally,通常用于释放资源,该方法一般在后置通知之前执行

    @AfterThrowing 异常通知  ,有异常才会通知

    2 需要了解的概念

    切面(aspect) 通常是指通知的方法所在的类,这个类就是切面类,也可称为切面

    连接点(JoinPoint)就是在切面类里面,把切入点(通常是个注解)和通知方法关联的那段代码,一个上面有注解的空方法

    通知(Advice)就是对于这个切点要做的事的所在方法

    切入点(PointCut)就是你的切面要作用在哪些方法,就是说你这个注解放在哪,通常是一组方法

    目标对象(target)就是要作用的对象(方法,属性,或类)

    二 代码实现

    1 自定义注解

    /**
    * 系统日志注解
    *
    * @author xuxiaorong
    */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Syslog1 {
    String value() default "默认值";
    }
    2 切面
    @Aspect
    @Component
    public class SysLogAspect1 {//这个类就是切面
    Logger logger = LoggerFactory.getLogger(SysLogAspect1.class);

    @Pointcut("@annotation(com.fh.annotation.Syslog1)")//注解其实就是切点
    public void log1PointCut() {
    System.out.println("切点里面");
    }
    @Before("log1PointCut()")//前置通知
    public void testBefore(JoinPoint point){//JoinPoint就是连接点,切点的实现方法就是通知
    System.out.println("加在注解上的前置方法");
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    //请求的方法名
    String className = point.getTarget().getClass().getName();
    String methodName = signature.getName();
    StringBuilder info = new StringBuilder();
    info.append("参数:")
    .append(JSON.toJSONString(((ServletRequestAttributes) RequestContextHolder
    .getRequestAttributes()).getRequest().getParameterMap()));
    System.out.println(info);//获取请求参数
    }
    @Before("within(com.fh.controller.cuijimanage.CuijiManageController*)")//前置通知,可以用类路径的形式来写
    public void log1(JoinPoint point){
    System.out.println("--加在类路径的前置方法");
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    //请求的方法名
    String className = point.getTarget().getClass().getName();
    String methodName = signature.getName();
    StringBuilder info = new StringBuilder();
    info.append("参数:")
    .append(JSON.toJSONString(((ServletRequestAttributes) RequestContextHolder
    .getRequestAttributes()).getRequest().getParameterMap()));
    System.out.println(info);
    }
    @AfterReturning( pointcut= "execution(* com.fh.controller.cuijimanage.CuijiManageController.*(..))", returning="returnValue")//后置通知
    public void log(JoinPoint point, Object returnValue) {
    System.out.println("后置通知");
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    //请求的方法名
    String className = point.getTarget().getClass().getName();
    String methodName = signature.getName();
    StringBuilder info = new StringBuilder();
    info.append("参数:")
    .append(JSON.toJSONString(((ServletRequestAttributes) RequestContextHolder
    .getRequestAttributes()).getRequest().getParameterMap()));
    System.out.println(info);//获取方法的请求参数
    System.out.println("返回值:"+JSON.toJSON(returnValue));//获取方法的返回值
    }
    @After("execution(* com.fh.controller.cuijimanage.CuijiManageController.*(..))")//最终通知,一般用于释放资源
    public void releaseResource(JoinPoint point) {
    System.out.println("最终方法");
    System.out.println("@After:模拟释放资源...");
    System.out.println("@After:目标方法为:" +
    point.getSignature().getDeclaringTypeName() +
    "." + point.getSignature().getName());
    StringBuilder info = new StringBuilder();
    info.append("参数:")
    .append(JSON.toJSONString(((ServletRequestAttributes) RequestContextHolder
    .getRequestAttributes()).getRequest().getParameterMap()));
    System.out.println("@After:参数为:" + info );//同样能获取方法的请求参数,一般不这样做
    System.out.println("@After:被织入的目标对象为:" + point.getTarget().getClass().getName());
    }
    @Around("log1PointCut()")//环绕通知
    public Object around(ProceedingJoinPoint point) throws Throwable {
    System.out.println("环绕通知");
    long beginTime = System.currentTimeMillis();
    //获取签名
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    //请求的方法名
    String className = point.getTarget().getClass().getName();
    String methodName = signature.getName();
    Syslog1 syslogAnnotation = method.getAnnotation(Syslog1.class);
    Object result = null;
    //操作记录 参数+数据
    StringBuilder info = new StringBuilder();
    Integer status = new Integer(0);
    //设置IP地址 操作人+Ip
    String ip = "";
    try {
    //系统注销时获取
    ip = "192.168.188.133";
    //执行方法
    result = point.proceed();
    try {
    info.append("参数:")
    .append(JSON.toJSONString(((ServletRequestAttributes) RequestContextHolder
    .getRequestAttributes()).getRequest().getParameterMap()))//获取方法的请求参数
    .append(";返回值:")
    .append(result == null ? "" : (String) JsonUtil.toJsonString(result));//获取方法的返回值
    if (result instanceof ResultJSON && JSON.toJSONString(result).contains("false")) {
    status = 1;
    }
    } catch (Exception ex) {
    logger.error("转json异常", ex);
    info.setLength(0);
    info.append("异常:转json异常");
    status = 1;
    }

    } catch (Throwable ex) {//异常通知
    logger.error(className + "." + methodName + "():执行异常" + ex.getMessage(), ex);
    info.setLength(0);
    info.append("异常:" + JSON.toJSONString(ex.getStackTrace()[0]));
    status = 1;
    }
    //执行时长(毫秒)
    long time = System.currentTimeMillis() - beginTime;
    logger.info("操作日志信息{" + className + "." + methodName
    + "():执行" + syslogAnnotation.value() + "耗时" + "" + time + "毫秒}");
    return result;
    }
    }
    3 切点
    /**
    * 发短信,动作,0 立即发送 1 定时发送
    */
    @Syslog1("催计-发短信")//这是一个切点
    @RequestMapping(value = "/doMessage.do")
    @ResponseBody
    public Map<String, Object> doMessage(HttpServletRequest request) throws Exception {}

    @Syslog1("催收详情-新增催收记录") //又一个切点,实现日志监控,只需加一个注解即可
    @RequestMapping(value = "addCuishouRecord.do", method = RequestMethod.POST)
    public ModelAndView addRecord(CallLoanLog callLoanLog) {}
    4 效果展现

       环绕通知
      --加在类路径的前置方法
      参数:{"contType":["2"],"contUserName":["唐豆豆1"],"tellPhone":["13524929813"],"callLoanNid":["2017101700005"],"callUser":["503"],"realName":["陈浩"],"mode":["0"],"message":["短信模板"],"sendTime":[""],"sendmode":["0"]}
      加在注解上的前置方法
      参数:{"contType":["2"],"contUserName":["唐豆豆1"],"tellPhone":["13524929813"],"callLoanNid":["2017101700005"],"callUser":["503"],"realName":["陈浩"],"mode":["0"],"message":["短信模板"],"sendTime":[""],"sendmode":["0"]}

    最终通知
    @After:模拟释放资源...
    @After:目标方法为:com.fh.controller.cuijimanage.CuijiManageController.doMessage
    @After:参数为:参数:{"contType":["2"],"contUserName":["唐豆豆1"],"tellPhone":["13524929813"],"callLoanNid":["2017101700005"],"callUser":["503"],"realName":["陈浩"],"mode":["0"],"message":["短信模板"],"sendTime":[""],"sendmode":["0"]}
    @After:被织入的目标对象为:com.fh.controller.cuijimanage.CuijiManageController
    后置通知
    参数:{"contType":["2"],"contUserName":["唐豆豆1"],"tellPhone":["13524929813"],"callLoanNid":["2017101700005"],"callUser":["503"],"realName":["陈浩"],"mode":["0"],"message":["短信模板"],"sendTime":[""],"sendmode":["0"]}
    返回值:{"num":1}

    5 结论

     1先执行环绕通知的 point.proceed()之前的部分,2然后执行前置通知,3执行目标方法,4执行最终通知,5执行后置通知

  • 相关阅读:
    指针和数组分析(上)
    函数对象分析
    C语言关键字
    STM32串口遇到的一个问题
    map文件分析
    对象的销毁
    对象的构造顺序
    JavaScript实现表单的校验以及匹配正则表达式
    Python Turtle库绘制表盘时钟
    Python Turtle库绘制蟒蛇
  • 原文地址:https://www.cnblogs.com/zhaoblog/p/7909640.html
Copyright © 2011-2022 走看看