聚合支付中,当用户使用微信APP扫码时(码为聚合系统生成的聚合码,非微信原生码,最终支付需要使用微信JSAPI支付方式),需要进行重定向到微信侧获取用户支付code,然后通过微信后台http接口使用code换取openId,然后进行支付.由于根据业务需求,需要进行两次重定向(第一次使用支付时使用的支付APPID,第二次使用大的公众号进行重定向),由于担心两次重定向相关流程处理时间较长,所以需要通过唯一请求流水号进行相关请求跟踪:业务具体流程为:
由于需要监控除http请求入口以及出口相关执行时间还需要监控某个service相关方法的执行时间,所以需要使用Spring Web MVC的处理器拦截器,以及aspect AOP切面编程具体了解,请详阅
相关url:https://jinnianshilongnian.iteye.com/blog/1670856
具体代码
public class TimeRecordWebInterceptor extends HandlerInterceptorAdapter { private static final Logger logger = LoggerFactory.getLogger(TimeRecordWebInterceptor.class); @Resource(name = "sequenceGenerator") private SequenceGenerator sequenceGenerator; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { try { String sequenceId = StringUtils.isNotBlank(request.getParameter(Constants.SEQUENCEID_KEY)) ? request.getParameter(Constants.SEQUENCEID_KEY) : "seq" + sequenceGenerator.createSequenceForPOS(); ThreadLocalTimeRecord.setSequenceId(sequenceId); } catch (Exception e) { logger.error("在controller请求preHandle生成sequenceId时,发生异常,异常信息:", e); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //防止内存溢出 ThreadLocalTimeRecord.removeSequenceId(); } }
spring-mvc.xml文件中拦截器配置:
<mvc:interceptors> <!--扫码支付以及微信等回调生成唯一请求sequenceId,跟踪请求使用--> <mvc:interceptor> <mvc:mapping path="/s"/> <mvc:mapping path="/p"/> <mvc:mapping path="/t"/> <mvc:mapping path="/*CallBack"/> <bean class="com.jd.jr.aggre.front.web.interceptor.TimeRecordWebInterceptor"/> </mvc:interceptor> </mvc:interceptors>
/** * 〈一句话功能简述〉<br> * 〈方法执行时间流水号存储计时器〉 * * @author guojunliang * @create 2019/3/25 * @since 1.0.0 */ public class ThreadLocalTimeRecord { private static final ThreadLocal<String> sequenceIdLocal = new ThreadLocal<String>(); public static String getSequenceId() { return sequenceIdLocal.get(); } public static void setSequenceId(String sequenceId) { sequenceIdLocal.set(sequenceId); } public static void removeSequenceId() { sequenceIdLocal.remove(); } }
aspectJ相关AOP切面定义:
@Aspect @Component public class TimeRecordInterceptor { private static final Logger logger = LoggerFactory.getLogger(TimeRecordInterceptor.class); @Pointcut("@annotation(com.jd.jr.aggre.front.common.interceptor.TimeRecord)") public void aopPoint() { } @Around("aopPoint()") public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = ""; long startTime = System.currentTimeMillis(); try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); methodName = signature.getDeclaringTypeName() + "." + signature.getName(); logger.info("##执行{}方法开始,请求标识sequenceId={},执行时间{}", methodName, ThreadLocalTimeRecord.getSequenceId(), DateUtil.date2String(new Date(), DateUtil.PATTERN_STANDARD23W)); } catch (Exception e) { logger.error("统计某方法执行耗时环绕通知方法执行前出错", e); } try { return joinPoint.proceed(joinPoint.getArgs()); } finally { logger.info("##执行{}方法结束,请求标识sequenceId={},执行时间={},耗时{}ms", methodName, ThreadLocalTimeRecord.getSequenceId(), DateUtil.date2String(new Date(), DateUtil.PATTERN_STANDARD23W), System.currentTimeMillis() - startTime); } } }
/** * 方法执行时长注解定义 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TimeRecord { }
由于重定向到微信或者支付宝或者京东支付时,拼接回调url编码方式不同,所以不能在springMVC中postHandle中进行modelAndView重定向页面进行拼接参数,需要在service中组装回调url时,进行请求号拼接:
/** * 重新拼接回调url,增加请求sequenceId,为记录时长使用 * * @param callBackUrl * @return */ String addSequenceIdForCallBackUrl(String callBackUrl) { if (StringUtils.isNotBlank(callBackUrl) && StringUtils.isNotBlank(ThreadLocalTimeRecord.getSequenceId())) { StringBuilder stringBuilder = new StringBuilder(callBackUrl); stringBuilder.append("&") .append(Constants.SEQUENCEID_KEY) .append("=") .append(ThreadLocalTimeRecord.getSequenceId()); return stringBuilder.toString(); } else { return callBackUrl; } }
执行结果: