springmvc参数注入源码剖析&源码扩展

项目中使用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;

    @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...
    }

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值