Spring MVC经过三个版本,功能已经改进和完善了很多。尤其是2.5以来采用的Annotation的参数绑定,极大的方便了开发,3.0对其进行更进一步的完善。
Spring MVC经过三个版本,功能已经改进和完善了很多。尤其是2.5以来采用的Annotation的参数绑定,极大的方便了开发,3.0对其进行更进一步的完善。对于一些特殊的前台框架,传到后台的不是普通的request中的参数,而是request流中的xml格式,这时就不能采用SpringMVC自带的参数绑定方法。这时候考虑是否能扩展一下。
SpringMVC默认使用的是AnnotationMethodHandlerAdapter.java,可以修改这个类来实现扩展。关键位置在如下方法中:
1. otected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
2.
3. ServletHandlerMethodResolver methodResolver= getMethodResolver(handler);
4.
5. Method handlerMethod = methodResolver.resolveHandlerMethod(request);
6.
7. ServletHandlerMethodInvoker methodInvoker=new ServletHandlerMethodInvoker(methodResolver);
8.
9. ServletWebRequest webRequest = new ServletWebRequest(request, response);
10.
11. ExtendedModelMap implicitModel = new BindingAwareModelMap();
12.
13. Object result=methodInvoker.invokeHandlerMethod(handlerMethod,handler,webRequest, implicitModel);
14.
15. ModelAndView mav=methodInvoker.getModelAndView(handlerMethod,handler.getClass(),result, implicitModel, webRequest); methodInvoker.updateModelAttributes(handler,(mav!=null?mav.getModel():null),implicitModel,webRequest);
16.
17. return mav;
18. }
蓝色位置是关键点,ServletHandlerMethodInvoker.java是内部类,继承自HandlerMethodInvoker.java,invokeHandlerMethod方法需要扩展,继续跟踪这个方法,发现是HandlerMethodInvoker.java这个类的方法,这个方法中的关键方法是resolveHandlerArguments(),关键部分如下:
1. if (RequestParam.class.isInstance(paramAnn)) {
2. RequestParam requestParam = (RequestParam) paramAnn;
3. paramName = requestParam.value();
4. required = requestParam.required();
5. defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
6. annotationsFound++;
7. }
8. else if (RequestHeader.class.isInstance(paramAnn)) {
9. RequestHeader requestHeader = (RequestHeader) paramAnn;
10. headerName = requestHeader.value();
11. required = requestHeader.required();
12. defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
13. annotationsFound++;
14. }
到此扩展的话需要添加自己的类型,如RequestParamExt,添加在后面,模仿如下:
1. else if (RequestParamExt.class.isInstance(paramAnn)) {
2. RequestParamExtrequestParam = (RequestParamExt) paramAnn;
3. paramName = requestParam.value();
4. defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
5. miType = requestParam.type();
6. annotationsFound++;
7. }
在
1. else if (paramName != null) {
2. args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
3. }
这个方法上面添加扩展逻辑:
1. if(!RequestParamExt.TYPE_NONE.equals(miType)){
2. if(null == platformRequest){
3. HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
4. platformRequest = new PlatformRequest((HttpServletRequest)request, "utf-8");
5. platformRequest.receiveData();
6. }
7. if(RequestParamExt.TYPE_PLATFORMREQUEST.equals(miType)){
8. args[i] = platformRequest;
9. }
10. else if(RequestParamExt.TYPE_STR.equals(miType)){
11. args[i] = resolveRequestStrParamExt(platformRequest, methodParam);
12. }else{
13. args[i] = resolveRequestParamExt(miType,platformRequest,paramName, defaultValue, methodParam, webRequest, handler);
14. }
15. }
两个resolveRequest*Ext方法如下:
1. protected Object resolveRequestStrParamExt(PlatformRequest platformRequest, MethodParameter methodParam){
2. VariableList inVl = platformRequest.getVariableList();
3. String paraName = methodParam.getParameterName();
4. return inVl.getValueAsObject(paraName);
5. }
6. protected Object resolveRequestParamExt(String miType,PlatformRequest platformRequest, String paramName,
7. String defaultValue,MethodParameter methodParam,NativeWebRequest webRequest, Object handler)throws Exception{
8. if(StringUtils.isBlank(paramName)){
9. paramName = defaultValue;
10. }
11. Class<?> paramType = methodParam.getParameterType();
12. DatasetList inDl = platformRequest.getDatasetList();
13. VariableList inVl = platformRequest.getVariableList();
14. if(RequestParamExt.TYPE_DS.equals(miType)){//绑定的关键过程
15. Dataset ds = inDl.getDataset(paramName);
16. Object vo = paramType.newInstance();
17. MiPDataBinder dataBinder = new MiPDataBinder(vo, false);
18. dataBinder.bind(inVl);
19. return dataBinder.getTarget();
20. }
21. }
同时还需要一个annotation的定义:示例如下:
1. package com.company.springext.web.bind.annotation;
2. import java.lang.annotation.Documented;
3. import java.lang.annotation.ElementType;
4. import java.lang.annotation.Retention;
5. import java.lang.annotation.RetentionPolicy;
6. import java.lang.annotation.Target;
7. @Target(ElementType.PARAMETER)
8. @Retention(RetentionPolicy.RUNTIME)
9. @Documented
10. public @interface RequestParamExt {
11. public static final String TYPE_NONE = "none";
12. public static final String TYPE_DS = "ds";
13. public static final String TYPE_VL = "vl";
14. public static final String TYPE_STR = "string";
15. String type() default TYPE_NONE;
16. String value() default "";
17. String defaultValue() default "ds";
18. }
最后是修改Spring配置:
1. <bean class="com.company.springext.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterExt">
2. </bean>
到此就实现了自定义格式的数据绑定。
对于特定格式的输出,如果需要自定义的话,同样需要修改AnnotationMethodHandlerAdapterExt.java这个类,关键位置在getModelAndView()方法。在如下位置:
1. } else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
2. handleResponseBody(returnValue, webRequest);
3. return null;
4. }
添加自己的扩展方法:
1. else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBodyExt.class) != null) {
2. ResponseBodyExt bodyMi = AnnotationUtils.findAnnotation(handlerMethod, ResponseBodyExt.class);
3. handleResponseBodyExt(returnValue, webRequest, bodyMi);
4. return null;
5. }
定义handleResponseBodyExt方法:
1. private void handleResponseBodyExt(Object returnValue, ServletWebRequest webRequest, ResponseBodyMI bodyMi) throws Exception {
2. HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
3. writeWithExtConverters(returnValue, servletResponse, bodyMi);
4. }
writeWithExtConverters()方法如下:
1. private void writeWithExtConverters(Object returnValue, HttpServletResponse response, ResponseBodyMI bodyMi) throws Exception {
2. convertToXML(...);
3. };
使用方式如下:
1. @RequestMapping(value="/getContractList")
2. @ResponseBodyExt(isCheck=true, resultType="sql", sqlColumns="ID,TUREID")
3.
4. public Page<Contract> getContractList(@RequestParamExt(value = "ds_search", type = "ds") Contract cp) throws Exception {
5. Page<Contract> page = method1();
6. return page;
7. }