Spring MVC經過三個版本,功能已經改進和完善瞭很多。尤其是2.5以來采用的Annotation的參數綁定,極大的方便瞭開發,3.0對其進行更進一步的完善。對於一些特殊的前臺框架,傳到後臺的不是普通的request中的參數,而是request流中的xml格式,這時就不能采用SpringMVC自帶的參數綁定方法。這時候考慮是否能擴展一下。
SpringMVC默認使用的是AnnotationMethodHandlerAdapter.java,可以修改這個類來實現擴展。關鍵位置在如下方法中:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ServletHandlerMethodResolver methodResolver= getMethodResolver(handler);
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
ServletHandlerMethodInvoker methodInvoker=new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
Object result=methodInvoker.invokeHandlerMethod(handlerMethod,handler,webRequest, implicitModel);
ModelAndView mav=methodInvoker.getModelAndView(handlerMethod,handler.getClass(),result, implicitModel, webRequest); methodInvoker.updateModelAttributes(handler,(mav!=null?mav.getModel():null),implicitModel,webRequest);
return mav;
}
ServletHandlerMethodInvoker.java是內部類,繼承自HandlerMethodInvoker.java,invokeHandlerMethod方法需要擴展,繼續跟蹤這個方法,發現是HandlerMethodInvoker.java這個類的方法,這個方法中的關鍵方法是resolveHandlerArguments(),關鍵部分如下
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
paramName = requestParam.value();
required = requestParam.required();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
annotationsFound++;
}
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.value();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
到此擴展的話需要添加自己的類型,如RequestParamExt,添加在後面,模仿如下:
else if (RequestParamExt.class.isInstance(paramAnn)) {
RequestParamExtrequestParam = (RequestParamExt) paramAnn;
paramName = requestParam.value();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
miType = requestParam.type();
annotationsFound++;
}
在
else if (paramName != null) {
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
}
這個方法上面添加擴展邏輯:
if(!RequestParamExt.TYPE_NONE.equals(miType)){
if(null == platformRequest){
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
platformRequest = new PlatformRequest((HttpServletRequest)request, "utf-8");
platformRequest.receiveData();
}
if(RequestParamExt.TYPE_PLATFORMREQUEST.equals(miType)){
args[i] = platformRequest;
}
else if(RequestParamExt.TYPE_STR.equals(miType)){
args[i] = resolveRequestStrParamExt(platformRequest, methodParam);
}else{
args[i] = resolveRequestParamExt(miType,platformRequest,paramName, defaultValue, methodParam, webRequest, handler);
}
}
兩個resolveRequest*Ext方法如下:
protected Object resolveRequestStrParamExt(PlatformRequest platformRequest, MethodParameter methodParam){
VariableList inVl = platformRequest.getVariableList();
String paraName = methodParam.getParameterName();
return inVl.getValueAsObject(paraName);
}
protected Object resolveRequestParamExt(String miType,PlatformRequest platformRequest, String paramName,
String defaultValue,MethodParameter methodParam,NativeWebRequest webRequest, Object handler)throws Exception{
if(StringUtils.isBlank(paramName)){
paramName = defaultValue;
}
Class<?> paramType = methodParam.getParameterType();
DatasetList inDl = platformRequest.getDatasetList();
VariableList inVl = platformRequest.getVariableList();
if(RequestParamExt.TYPE_DS.equals(miType)){//綁定的關鍵過程
Dataset ds = inDl.getDataset(paramName);
Object vo = paramType.newInstance();
MiPDataBinder dataBinder = new MiPDataBinder(vo, false);
dataBinder.bind(inVl);
return dataBinder.getTarget();
}
}
同時還需要一個annotation的定義:示例如下:
package com.company.springext.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParamExt {
public static final String TYPE_NONE = "none";
public static final String TYPE_DS = "ds";
public static final String TYPE_VL = "vl";
public static final String TYPE_STR = "string";
String type() default TYPE_NONE;
String value() default "";
String defaultValue() default "ds";
}
最後是修改Spring配置:
<bean class="com.company.springext.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterExt">
</bean>
到此就實現瞭自定義格式的數據綁定。
對於特定格式的輸出,如果需要自定義的話,同樣需要修改AnnotationMethodHandlerAdapterExt.java這個類,關鍵位置在getModelAndView()方法。在如下位置:
} else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
添加自己的擴展方法:
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBodyExt.class) != null) {
ResponseBodyExt bodyMi = AnnotationUtils.findAnnotation(handlerMethod, ResponseBodyExt.class);
handleResponseBodyExt(returnValue, webRequest, bodyMi);
return null;
}
定義handleResponseBodyExt方法:
private void handleResponseBodyExt(Object returnValue, ServletWebRequest webRequest, ResponseBodyMI bodyMi) throws Exception {
HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
writeWithExtConverters(returnValue, servletResponse, bodyMi);
}
writeWithExtConverters()方法如下:
private void writeWithExtConverters(Object returnValue, HttpServletResponse response, ResponseBodyMI bodyMi) throws Exception {
convertToXML(…);
};
使用方式如下:
@RequestMapping(value="/getContractList")
@ResponseBodyExt(isCheck=true, resultType="sql", sqlColumns="ID,TUREID")
public Page<Contract> getContractList(@RequestParamExt(value = "ds_search", type = "ds") Contract cp) throws Exception {
Page<Contract> page = method1();
return page;
}
作者“菜園子”