`
chenruieye
  • 浏览: 36666 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

记我的第一次spring aop项目实践

    博客分类:
  • Java
阅读更多

一、需求背景。

 

1、刚来公司1个多月,接到一个新的项目需求,需要开发喜力啤酒的app相关接口,因为相对比较独立,具体功能领导安排我一个人完成。

 

然后就愉快的撸起了代码,撸啊撸,撸啊撸,时间过得真快,过了1周,就撸完了.屁颠屁颠跑过去给领导说撸完了,可以上线了。

 

领导思考了片刻,为保障上线需要,需要监控喜力所有接口的日志部分,包括请求参数和返回参数的情况。让我把喜力所有接口的相关调用日志也加上!

 

Oh,我滴个乖乖,这个咋整啊,难道要去每个接口加参数,那么多接口,都猴年马月去了。后来请教了一下公司的前辈,问他们怎么做,他们让我用spring aop来弄,只需要在需要监控的方法加上相关注解就可以了。于是开始了我的spring aop之旅。

 

 

二、希望实现的效果。

 

1、哪个接口需要记录日志,就在哪个日志前加一个注解,这样能做到比较灵活。毕竟不是所有接口都要加。

 

2、不改变已经写好了的接口的任何业务逻辑,不穿插代码。

 

3、对调用者调用服务端的接口依然和修改前调用方式一样,让client无感监控动作。

 

三、结论:

1、能满足第二点提到的要求,用spring aop再合适不过了。

 

2、首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题。这个时候就马上打开CRT或者SSH连上服务器拿日子来分析。受网络的各种限制。于是我们就想为什么不能直接在管理后台查看报错的信息呢。于是日志管理就出现了。

         其次个人觉得做日志管理最好的是Aop,有的人也喜欢用拦截器。都可以,在此我重点介绍我的实现方式。

         Aop有的人说拦截不到Controller。有的人说想拦AnnotationMethodHandlerAdapter截到Controller必须得拦截org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。

首先Aop可以拦截到Controller的,这个是毋容置疑的其次须拦截AnnotationMethodHandlerAdapter也不是必须的。最起码我没有验证成功过这个。我的Spring版本是4.0.3。

         Aop之所以有的人说拦截不到Controller是因为Controller被jdk代理了。我们只要把它交给cglib代理就可以了。

 

 

四、操作步骤。

 

第一步:定义注解:

 

package com.biz.core.util.annotation;

import java.lang.annotation.*;

//@Documented
//@Retention(RetentionPolicy.RUNTIME)
//@Target({ElementType.METHOD})

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebMapControllerLog {
    String module()  default "";
    String methods()  default "";
}

 

 

第二步:定义一个切点类

 

package com.biz.web.controller.util;


import com.alibaba.fastjson.JSONObject;
import com.biz.core.bean.MessageObject;
import com.biz.core.bean.enums.MessageType;
import com.biz.core.entity.WebMapLog;
import com.biz.core.util.annotation.HeineKenControllerLog;
import com.biz.core.util.annotation.WebMapControllerLog;
import com.biz.core.util.mq.MqUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@Aspect
@Component
public class WebMapControllerLogAspect {

    //本地异常日志记录对象
    private static final Logger logger = LoggerFactory.getLogger(WebMapControllerLogAspect.class);


    /**
     * 切入点,加了注解SfaMethod
     */
    // @Pointcut(value = "@annotation(com.biz.core.util.gatewaySupport.annotation.SfaMethod)")
    //@Pointcut(value = "@annotation(com.biz.core.util.annotation.HenineKenAnnotation)")
    @Pointcut("@annotation(com.biz.core.util.annotation.WebMapControllerLog)")
    public void serviceAspect() {
        System.out.println("我是一个切入点");
    }


    /**
     *     * 前置通知:目标方法执行之前执行以下方法体的内容      * @param jp    
     */
//    @Before("execution(* com.biz.service.rest.henineken.impl.*.*(..))")
// @Before(value = "serviceAspect()")
//    public void beforeMethod(JoinPoint joinPoint) {
//        String methodName = joinPoint.getSignature().getName();
//    begintime = System.currentTimeMillis();
//
//        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        HttpSession session = request.getSession();
//
//        String url = request.getRequestURL().toString();
//        String method = request.getMethod();
//        String uri = request.getRequestURI();
//        String a = request.getQueryString();
////        request.
//
//        Map<String,String[]> ss = request.getParameterMap();
//
//        Enumeration<String> s1 = request.getParameterNames();
//
//                //Map<String,Object> pramMap = request.getParameterMap();
//
//        logger.info(a);
//
//
//
////        //读取session中的用户 等其他和业务相关的信息,比如当前用户所在应用,以及其他信息, 例如ip
////        String ip = request.getRemoteAddr();
////
////
////        JSONObject jdata = JSONObject.parseObject(data);
////        try {
////            logger.info("doBefore enter。 任何时候进入连接点都调用");
////            logger.info("method requested:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
////            logger.info("method description:" + getServiceMethodDescription(joinPoint));
////            logger.info("remote ip:" + ip);
////            //日志存入数据库
////
////            logger.info("doBefore end");
////        }  catch (Exception e) {
////            logger.info("doBefore exception");
////            logger.info("exceptionMsg={}", e.getMessage());
////        }
//
//        System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + JSONObject.toJSONString(joinPoint.getArgs()));
//    }
//
//    /**
//     *     * 返回通知:目标方法正常执行完毕时执行以下代码     * @param jp     * @param result    
//     */
////    @AfterReturning( value = "execution(* com.biz.service.rest.henineken.impl.*.*(..))", returning = "result")
// @AfterReturning(pointcut = "serviceAspect()", returning = "result")
//    public void afterReturningMethod(JoinPoint jp, Object result) {
//        String methodName = jp.getSignature().getName();
//
//        System.out.println("【返回通知1】the method 【" + methodName + "】 ends with 【" + result + "】");
//
//    CrmReqLog crmReqLog=new CrmReqLog();
//    crmReqLog.setContent(JSONObject.toJSONString(result));
//    crmReqLog.setInfo("格式化请求内容");
//    crmReqLog.setUri("workCircleSummaryLikeService.1111");
//    crmReqLog.setTimes(System.currentTimeMillis()-begintime);
//
//    crmReqLog.setSuccess(true);
//    MqUtil.sendMessage(MessageObject.getLogType(MessageType.crm_req, crmReqLog));
// }
    @Around("serviceAspect()   && @annotation(oauth)")
    public Object around(ProceedingJoinPoint pjp, WebMapControllerLog oauth) throws Throwable {


        logger.info("进入环绕通知");
        Object[] args = pjp.getArgs(); //7

        String uri = getRequestURI();





        HttpServletRequest request = (HttpServletRequest) args[0];
        Map<String, Object> param = getParameterMap(request);


        long beginTime = System.currentTimeMillis();
        Object objectResult = pjp.proceed();
        logger.info("执行完方法调用");
        WebMapLog webMapLog = new WebMapLog();
        webMapLog.setUri(uri);
        webMapLog.setReq_param(JSONObject.toJSONString(param));
        String content = JSONObject.toJSONString(objectResult);
        //当前报文长度
        int length = content.length();
        webMapLog.setLengths(length);
        webMapLog.setReturn_param(content);
        webMapLog.setTimes(System.currentTimeMillis() - beginTime);
        webMapLog.setMethod(oauth.methods());
        webMapLog.setModule(oauth.module());
        //日志
        webMapLog.setSuccess(true);
        webMapLog.setSno("sno");
        MqUtil.sendMessage(MessageObject.getLogType(MessageType.web_map, webMapLog));
        logger.info("this is after around advice normal end");


//        String sno = (String) param.get("sno");
//        String lockName = RedisLockUtil.getLockName(uri, sno);
//
//        long beginTime = System.currentTimeMillis();
//        Object objectResult = null;
//        boolean isok = false;
//        try {
//            if (StringUtils.isNotEmpty(lockName) && StringUtils.isNotEmpty(sno)) {
//                //判断是否有处理成功的
//                LockStatus lockStatus = RedisLockUtil.getLockStatus(lockName);
//                //处理成功的直接返回
//                if (LockStatus.success.equals(lockStatus)) {
//                    objectResult = JSONResult.successByMsg("该接口已成功处理过");
//                    isok = true;
//                } else if (LockStatus.loading.equals(lockStatus)) {
//                    //处理中
//                    objectResult = JSONResult.error(Exceptions.Global.SERVICE_LOADING.getCode(), Exceptions.Global.SERVICE_LOADING.getDescription());
//                } else {
//                    //获取锁,开始处理
//                    boolean successLock = RedisLockUtil.getLock(lockName, 2, 24 * 60 * 60);
//                    //获得锁,处理
//                    if (successLock) {
//                        //成功的不再处理(必须二次判断)
//                        if (LockStatus.success.equals(RedisLockUtil.getLockStatus(lockName))) {
//                            objectResult = JSONResult.successByMsg("该接口已成功处理过");
//                            isok = true;
//                        }else{
//                            objectResult = pjp.proceed();
//                            isok = true;
//                            //解锁
//                            RedisLockUtil.setLockStatus(lockName, LockStatus.success);
//                            //同时关闭同步锁
//                            RedisLockUtil.unLock(lockName);
//                        }
//                    } else {
//                        //没有获得锁也是处理中
//                        objectResult = JSONResult.error(Exceptions.Global.SERVICE_LOADING.getCode(), Exceptions.Global.SERVICE_LOADING.getDescription());
//                    }
//                }
//            } else {
//                //sno不存在
//                objectResult = pjp.proceed();
//                isok = true;
//            }
//        } catch (Throwable t) {
//            t.printStackTrace();
//            logger.error(t.getMessage());
//            //失败了
//            isok = false;
//            JSONResult jerror=null;
//            if (null != t.getCause()) {
//                if (t.getCause().getClass().equals(CRMException.class)) {
//                    CRMException crmException = (CRMException) t.getCause();
//                    jerror = crmException.getResult();
//                } else {
//                    jerror = JSONResult.error(Exceptions.Global.ERROR.getCode(), "服务器正忙,请稍后再试");
//                }
//            }
//            //错误配置
//            if (null == jerror || StringUtils.isEmpty(jerror.getMsg())) {
//                jerror = JSONResult.error(Exceptions.Global.INTERFACE_SERVER_EXCEPTION.getCode(), Exceptions.Global.INTERFACE_SERVER_EXCEPTION.getDescription());
//            }
//
//            objectResult = jerror;
//            logger.error(":调用接口出错:" + jerror.toString() + ",接口编号:" + sno);
//
//            if (StringUtils.isNotEmpty(lockName)) {
//                //解锁
//                RedisLockUtil.setLockStatus(lockName, LockStatus.fail);
//                //同时关闭同步锁
//                RedisLockUtil.unLock(lockName);
//            }
//        } finally {
//            //关闭crm日志 20200323
//
//
//            WebMapLog webMapLog=new WebMapLog();
//            webMapLog.setUri(uri);
//            webMapLog.setReq_param(JSONObject.toJSONString(param));
//            String content = JSONObject.toJSONString(objectResult);
//            //当前报文长度
//            int length = content.length();
//            webMapLog.setLengths(length);
//            webMapLog.setReturn_param(content);
//            webMapLog.setTimes(System.currentTimeMillis()-beginTime);
//            //日志
//            webMapLog.setStatus(isok);
//            webMapLog.setSno(sno);
//            MqUtil.sendMessage(MessageObject.getLogType(MessageType.web_map, webMapLog));
//            logger.info("this is after around advice normal end");
//        }

        return objectResult;
    }


    private String getRequestURI() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String uri = request.getRequestURI();
        return uri;
    }


    public Map<String, Object> getParameterMap(HttpServletRequest request) {
        // 参数Map
        Map<?, ?> properties = request.getParameterMap();
        // 返回值Map
        Map<String, Object> returnMap = new HashMap<String, Object>();
        Iterator<?> entries = properties.entrySet().iterator();

        Map.Entry<String, Object> entry;
        String name = "";
        String value = "";
        Object valueObj = null;
        while (entries.hasNext()) {
            entry = (Map.Entry<String, Object>) entries.next();
            name = (String) entry.getKey();
            valueObj = entry.getValue();
            if (null == valueObj) {
                value = "";
            } else if (valueObj instanceof String[]) {
                String[] values = (String[]) valueObj;
                for (int i = 0; i < values.length; i++) {
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length() - 1);
            } else {
                value = valueObj.toString();
            }
            returnMap.put(name, value);
        }
        return returnMap;
    }


//    /**
//     *     * 异常通知:目标方法发生异常的时候执行以下代码    
//     */
//    @AfterThrowing(value = "execution(* com.biz.service.rest.henineken.impl.*.*(..))", throwing = "e")
//    public void afterThorwingMethod(JoinPoint jp, NullPointerException e) {
//        String methodName = jp.getSignature().getName();
//        System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);
//
//    }


//    //异常通知
//    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
//    public void handleThrowing(JoinPoint joinPoint, Exception e) {
//
//        Signature signature = joinPoint.getSignature();
//        MethodSignature methodSignature = (MethodSignature) signature;
//        IntfaceException intfaceException = methodSignature.getMethod().getAnnotation(IntfaceException.class);
//
//        //连接异常进行短信通知
//        if (e instanceof CRMException) {
//
//            CRMException crmExcep = (CRMException) e;
//            JSONResult result = crmExcep.getResult();
//
//            TxySmsSetting txySmsSetting = SettingUtil.getTxySmsSetting();
//
//
//            if (result.getErrcode() == 1107) {
//
//                Object notify = RedisUtil.get("sys_excep_notify" + intfaceException.system());
//
//                if (notify != null) {
//                    return;
//                }
//
//                String[] content = new String[3];
//
//                content[0] = SystemConstants.profileName + "" + intfaceException.system();
//                content[1] = result.getMsg();
//                content[2] = intfaceException.module();
//
//                String[] phones = txySmsSetting.getExcepPhones().split(",");
//
//                SmsMessage smsMessage = new SmsMessage();
//                smsMessage.setAppuser("admin");
//                smsMessage.setContent(content);
//                smsMessage.setPhones(phones);
//                smsMessage.setTemplateId(txySmsSetting.getIntfacExcepTemplateId());
//                //12小时之内不重复发送短信
//                RedisUtil.set("sys_excep_notify" + intfaceException.system(), "Y", 43200L);
//                MqUtil.sendMessage(MessageObject.getType(null, smsMessage, MqConstant.mq_sms_send));
//
//            }
//
//        }
//
//
//    }


    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    private static String getServiceMethodDescription(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(HeineKenControllerLog.class).module();
                    break;
                }
            }
        }
        return description;
    }


}

 

 

 

第三步:把Controller的代理权交给cglib

在实例化ApplicationContext的时候需要加上

 

Xml代码 复制代码
  1. <!-- 启动对@AspectJ注解的支持 -->  
  2. <aop:aspectj-autoproxy/>  
  3. <aop:aspectj-autoproxy proxy-target-class="true" />

 在调用Controller的时候AOP发挥作用所以在SpringMVC的配置文件里加上

 

因为 SpringMVC的Contrller的容器和 spring 的容器不是同一个,必须再spring mvc的配置文件添加<aop:aspectj-autoproxy proxy-target-class="true" />

 这个语句,才能实现对controller的注解支持

 

第四步使用

 

Controller层的使用

 

@RequestMapping(method = RequestMethod.GET)
@WebMapControllerLog(methods = "goLogin",module = "跳转登录界面")
public String goLogin(HttpServletRequest request, HttpServletResponse response) {
    logger.debug("LoginController  goLogin");
    return BaseUntil.webPath + "/login/loginMain";
}

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics