springmvc源码扩展——自定义参数解析
来源:互联网 发布:又拍云存储 域名 编辑:程序博客网 时间:2024/06/08 12:00
项目中使用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)@Documentedpublic @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; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ( bean instanceof RequestMappingHandlerAdapter ) { RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter)bean; registerArgumentsResolvers( adapter ); registerReturnValueHandlers( adapter ); } return bean; } @Override public 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参数中SessionUser HandlerMethodArgumentResolver 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; } @Override public 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源码扩展——自定义参数解析
- SpringMVC 源码解析AbstractCachingViewResolver自定义缓存
- springMVC源码解析--HandlerMethodArgumentResolverComposite参数解析器集合(二)
- springmvc属性编辑器和自定义参数解析器
- springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)
- springMVC源码分析--RequestParamMethodArgumentResolver参数解析器(三)
- springmvc参数收集与类型转换源码解析
- SpringMVC自定义参数绑定
- springmvc源码解析(1)
- Spring源码解析-springmvc
- SpringMVC源码解析-ContentNegotiationStrategy
- SpringMVC源码解析-HandlerInterceptor
- SpringMVC源码解析-LocaleResolver
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- springMVC使用HandlerMethodArgumentResolver 自定义解析器实现请求参数绑定方法参数
- hdu_2187_水
- Eclipse上Maven环境配置使用 (全)
- ios 微信返回页面标题无更改更新
- mysqldump: Got error: 1290: The MySQL server is running with the --secure-file-priv option so it can
- linux下动态库so文件的一些认识
- springmvc源码扩展——自定义参数解析
- 《机器学习实战》学习笔记1
- POJ 1222 EXTENDED LIGHTS OUT
- android复制assets里的压缩文件到sdcard里并解压
- Groovy语法之闭包
- xxx cannot be resolved to a type
- js基本功—干货
- Java 自带的线程池Executors.newFixedThreadPool
- [NOIP模拟赛]同色齿轮问题