项目中使用json传递数据,定义了一套统一的格式,如下所示,但是实际上业务层面只需要处理data节点的数据,sign、signType参数由框架层面进行验签处理。项目中的web层使用了springmvc、resteasy框架,为了方便接收data节点的json数据,笔者扩展了springmvc的源码,添加了自定义的HandlerMethodArgumentResolver。

{"sign":"xxx","signType":"xxx","data":{"partnerId":"xxx",......}
}

springmvc部分源码分析

下面是springmvc处理请求的核心流程

先是根据HttpServletRequest遍历所有的HandlerMapping,常用的实现类有RequestMappingHandlerMapping、BeanNameUrlHandlerMapping,调用其getHandler方法获取HandlerExecutionChain,这个对象里面包括了我们熟悉的HandlerInteceptor拦截器,会在处理请求前、后、产生响应的时候被调用。然后,根据HandlerExecutionChain的Handler实例,获取HandlerAdapter,同样的,也是遍历List,如果HandlerAdapter.supports(handler)则返回,比如RequestMappingHandlerAdapter、HttpRequestHandlerAdapter。接下来,将请求交给HandlerAdapter处理,返回ModelAndView。
HanderAdapter是调用Controller的核心接口,由它负责处理请求。RequestMappingHandlerAdapter是HandlerAdapter的子类,它支持对HandlerMethod的处理,并进行参数处理、执行HandlerMethod,处理响应数据等。它包括了常见的参数解析器(HandlerMethodArgumentResolver),例如对@RequestBody、@Path的处理,大家可以看下它的实现类。另外,还有方法返回数据的处理类(HandlerMethodReturnValueHandler)。我们现在需要对入参进行处理,因此需要实现HandlerMethodArgumentResolver接口。如果对响应数据进行处理,需要实现HandlerMethodReturnValueHandler。
关键是怎么将自定义的HandlerMethodArgumentResolver添加到RequestMappingHandlerAdapter中?因为RequestMappingHandlerAdapter是受spring管理的类,如果是以xml配置springmvc的话,org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser在处理标签时会注册RequestMappingHandlerAdapter的bean定义。如果是以@EnableWebMvc注解配置springmvc的话,也会有这个类的定义。既然如此,搞个BeanPostProcessor应该是可以的。具体的代码,请笔者结合时序图进行阅读。

如何扩展

先自定义个注解,用来支持对json中的data节点进行处理

/*** 用于扩展SpringMVC的参数解析功能,只读取json串中的data节点作为Controller方法入参,eg:* public JsonResult pay( @RequestDataBody PayRequest request, Sign sign )* @author huangxf* @date 2017年4月10日*/
@Target({ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestDataBody {boolean required() default true;}

写个BeanPostProcessor实现postProcessBeforeInitialization方法,并且将这个bean交给spring管理即可。这样,我们在RequestMappingHandlerAdapter初始化之前,便可以添加自定义的参数解析器,如下所示。我的github提供的代码,支持SessionUser(接口)、Sign(接口)、@RequestDataBody的处理,相关的代码在net.dwade.plugins.spring.web这个包下面,github地址:https://github.com/huangxfchn/dwade/tree/master/framework-plugins

/**
* 使用{@link BeanPostProcessor}对SpringMVC进行扩展,
* 支持{@link RequestDataBody}、{@link Sign}、{@link SessionUser}、{@link CheckSign}、{@link SignResponseBody}
* @see RequestMappingHandlerAdapter
* @see BeanPostProcessor
* @author huangxf
* @date 2017年4月13日
*/
public class PaymentControllerSupport implements BeanPostProcessor, ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if ( bean instanceof RequestMappingHandlerAdapter ) {RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter)bean;registerArgumentsResolvers( adapter );registerReturnValueHandlers( adapter );}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {return bean;}/*** 注册参数解析器 * @param adapter* @return void*/private void registerArgumentsResolvers( RequestMappingHandlerAdapter adapter ) {List<HandlerMethodArgumentResolver> resolvers = this.getCustomerArgumentResolvers( adapter );if ( adapter.getCustomArgumentResolvers() == null ) {adapter.setCustomArgumentResolvers( resolvers );} else {adapter.getCustomArgumentResolvers().addAll( resolvers );}}/*** 注册返回值处理器* @param adapter* @return void*/private void registerReturnValueHandlers( RequestMappingHandlerAdapter adapter ) {List<HandlerMethodReturnValueHandler> resolvers = this.getCustomerReturnValueHandler( adapter );if ( adapter.getCustomReturnValueHandlers() == null ) {adapter.setCustomReturnValueHandlers( resolvers );} else {adapter.getCustomReturnValueHandlers().addAll( resolvers );}}protected List<HandlerMethodArgumentResolver> getCustomerArgumentResolvers( RequestMappingHandlerAdapter adapter ) {//处理method参数中SessionUserHandlerMethodArgumentResolver sessionUserResolver = new SessionUserResolver();applicationContext.getAutowireCapableBeanFactory().initializeBean( sessionUserResolver, SessionUserResolver.class.getName() );//对请求参数进行处理,解析data节点、签名HandlerMethodArgumentResolver requestDataResolver = new RequestDataConverterProcessor( adapter.getMessageConverters() );applicationContext.getAutowireCapableBeanFactory().initializeBean( requestDataResolver, RequestDataConverterProcessor.class.getName() );//处理验签HandlerMethodArgumentResolver signResolver = new SignResponseProcessor( adapter.getMessageConverters() );applicationContext.getAutowireCapableBeanFactory().initializeBean( signResolver, SignResponseProcessor.class.getName() );List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();resolvers.add( sessionUserResolver );resolvers.add( requestDataResolver );resolvers.add( signResolver );return resolvers;}/*** 获取返回参数处理的HandlerMethodReturnValueHandler实现类* @param adapter* @return List<HandlerMethodReturnValueHandler>*/protected List<HandlerMethodReturnValueHandler> getCustomerReturnValueHandler( RequestMappingHandlerAdapter adapter ) {//处理签名、验签HandlerMethodReturnValueHandler signHandler = new SignResponseProcessor( adapter.getMessageConverters() );applicationContext.getAutowireCapableBeanFactory().initializeBean( signHandler, SignResponseProcessor.class.getName() );List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();handlers.add( signHandler );return handlers;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext)throws BeansException {this.applicationContext = applicationContext;}}

如何使用

json报文如下,假设我需要将data节点,使用PayOffRequest对象接收,只需要添加@RequestDataBody注解即可

{"sign":"xxx","signType":"xxx","data":{"partnerId":"xxx","money":10000}
}

Controller代码

@Controller
@RequestMapping( "/pay" )
public class PaymentController {private final Logger logger = LoggerFactory.getLogger( PaymentController.class );/*** <code>@CheckSign</code>:需要验签,<code>@SignResponseBody</code>:响应的数据需要签名处理* @param request    * @param user* @return PaymentResponse<PayOffResponse>*/@RequestMapping(value="payoff", method=RequestMethod.POST)public PaymentResponse<PayOffResponse> payOff( @RequestDataBody PayOffRequest request, HttpServletRequest httpRequest, SessionUser user ) {// your code...}}

springmvc参数注入源码剖析源码扩展相关推荐

  1. 并发编程之 ThreadLocal 源码剖析

    前言 首先看看 JDK 文档的描述: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局 ...

  2. php 转义md5 和java 转译的区别_CTF|PHP中的命令参数注入

    直奔主题,首先来了解两个 PHP 函数的定义. 命令参数注入 escapeshellarg 把字符串转码为可以在 shell 命令里使用的参数. escapeshellcmd 对字符串中可能会欺骗 s ...

  3. SpringMVC子父容器源码剖析

    SpringMVC子父容器源码剖析 一.子父容器启动流程 二.环境准备 spring源码搭建 spring-framework-5.1.x 源码编译 环境搭建 [ idea:2020.1 ] 在spr ...

  4. SpringDataJPA+Hibernate框架源码剖析(六)@PersistenceContext和@Autowired注入EntityManager的区别

    SpringDataJPA+Hibernate框架源码剖析系列文章: SpringDataJPA+Hibernate框架源码剖析(一)框架介绍 SpringDataJPA+Hibernate框架源码剖 ...

  5. 老李推荐:第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数...

    老李推荐:第8章2节<MonkeyRunner源码剖析>MonkeyRunner启动运行过程-解析处理命令行参数 MonkeyRunnerStarter是MonkeyRunner启动时的入 ...

  6. SpringBoot (一) 入门、配置、自动配置源码剖析理解

    文章目录 0 Spring Boot 1 Overview 1.1 Introduce **Spring** SpringBoot 微服务 1.2 快速上手 Hello World pom.xml s ...

  7. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 1...

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionPro ...

  8. Spring源码剖析——Bean的配置与启动

    IOC介绍   相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Sp ...

  9. Swoft 源码剖析 - Swoft 中的注解机制

    作者:bromine 链接:https://www.jianshu.com/p/ef7... 來源:简书 著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版. Swoft Github ...

最新文章

  1. pku The Windy's KM最小权匹配 or 最小费用最大流
  2. Windbg学习 (0x0002) 命令基础
  3. 使用wxSqlite3来解决sqlite加密问题zz
  4. InnerHTML、InnerText、outerHTML、outerText的区别
  5. breeze源码阅读心得
  6. java游戏一开始去山上打狐狸_。。。这才是Java的第一个程序------HelloWorld
  7. 如何快速截取某段时间内的日志
  8. 安装oracle 11g 客户端,检查过程中报物理内存不足的解决
  9. linux 编译工具链,Linux工具链for TKStudio下载_Linux工具链for TKStudio官方下载-太平洋下载中心...
  10. 《learn objective-c on the Mac for OS X and IOS》译名《objective-C基础教程》
  11. 数说:这只程序员组建的乐队为何能进HOT5?
  12. ProtoBuf和Netty的简单使用
  13. MTK6589编译出错
  14. 为什么MES系统等数字化管理系统,在印刷行业应用发展得如此迅速
  15. Hyper-v 实现与虚拟机共享文件夹
  16. 公众号运营-Datawhale-1
  17. 【Python实战】批量爬取微博素材,一分钟百张大图自动下载
  18. office2010案例一
  19. 计算机基础---03Excel篇(入门,身份证筛选出生年月,自动填充功能,表格排序,自动筛选,高级筛选,数据有效性,求和)
  20. 显示屏信号超过输入范围1600x900@60HZ ------解决办法

热门文章

  1. Stream流数组和对象List排序
  2. 【mysql】整数类型
  3. 基于YOLOv5实现中药饮片识别(含源码)【目标检测项目】
  4. 把时间当做朋友--摘录
  5. vmware配置双网卡
  6. winds10 关于绝地求生游戏中不能使用语音的解决方法
  7. ffmpeg入门教程
  8. 铺地砖(找递推式 + 大数)
  9. 蚁群算法详解-解决TSP问题
  10. 金山毒霸通行证申请小助手