一般在servlet或controller里获取前台请求的数据时时通过request.getParam的方式获取,此方式会产生大量的模板式的重复代码,整体代码也不简洁。为此各种框架都做了一些处理,使得开发人员从这种无聊的工作中解放出来,比如spring在handle方法中增加object参数,struct在action的execute方法中增加form参数。应用系统开发人员直接从object、form中获取所需数据。今天想分享下自己在实现这种映射时的思路和具体代码。
主要使用的技术就是java的反射编程(反射和动态代理是java技术的一大亮点,相当好用)。主要思路是通过编写一个抽象controller父类,并增加handleRequestForSpringWeb抽象方法,所有业务controller继承此父类,并重写handleRequestForSpringWeb,在该方法中处理业务逻辑,而spring提供的原有的抽象方法handleRequestInternal则用来处理请求参数与dto的绑定。绑定时的原则是先确认dto属性(具体如convertMap),再根据dto属性去request中获取参数值,并赋值dto属性中。
这里以重写spring的abstractcontroller为例,代码不算多,具体如下:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import dto.AbstractDTO;
public abstract class SpringWebAbstractController extends AbstractController {
private String dto;
public String getDto() {
return dto;
}
public void setDto(String dto)throws Exception {
this.dto = dto;
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
request.setCharacterEncoding("gbk");
response.setCharacterEncoding("utf-8");
AbstractDTO obj = (AbstractDTO)this.build(request);
return handleRequestForSpringWeb(request,response,obj);
}
//此抽象方法新增dto参数,方便取值,所有继承此类的controller,需配置dto属性
//若不配置,则不可使用dto
<span style="color:#ff0000;">public abstract ModelAndView handleRequestForSpringWeb(HttpServletRequest request,
HttpServletResponse response,AbstractDTO abstractDTO)throws Exception</span>;
private Object build(ServletRequest request)throws Exception{
Object instance = null;
try {
if(dto==null||dto.trim().equals(""))
return null;
Class<?> clazz = Class.forName(dto);
instance = clazz.newInstance();
Field fields[] = clazz.getFields();
Map<String,Class<?>> fieldMap = convertMap(fields);
Enumeration<?> enumeration = request.getParameterNames();
while(enumeration.hasMoreElements()){
String paramKey = (String)enumeration.nextElement();
String paramValues[] = request.getParameterValues(paramKey);
Class fieldClazz = fieldMap.get(paramKey);
if(fieldClazz==null){
//这里应该报错,提示开发人员注意dto与请求参数保持一致。
continue;
}
setProperty(instance,fieldClazz,paramKey,paramValues);
}
} catch (Exception e) {
throw e;
}
return instance;
}
/**
*把DTO的field按照key=name和value=class的形式转成map结构
*/
private Map<String,Class<?>> convertMap(Field fields[]){
Map<String,Class<?>> fieldMap = new HashMap<String,Class<?>>();
for(Field field:fields){
String key = field.getName();
Class<?> value = field.getType();
fieldMap.put(key, value);
}
return fieldMap;
}
private void setProperty(Object instance,Class fieldClass,String key,String values[]) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
String methodName = "set"+key.substring(0, 1).toUpperCase()+key.substring(1, key.length());
try {
Method method = instance.getClass().getMethod(methodName, fieldClass);
if(fieldClass.getName().equals("java.lang.String")){
method.invoke(instance, new Object[]{values[0]});
}else{
method.invoke(instance, new Object[]{values});
}
} catch (SecurityException e) {
e.printStackTrace<span style="color:#cc0000;">();//异常需要处理
</span> } catch (NoSuchMethodException e) {
e.printStackTrace();<span style="color:#cc0000;">//异常需要处理</span>
}
}
public void init(){
System.out.println(this.getClass().getName()+";dto="+this.getDto());
}
}
改进:
此代码有两个地方可以优化的地方
1、dto属性类型
目前的dto仅支持了string和string数组(这个也是request中的请求参数类型),但是实际中可能需要转成date,int甚至其他dto等多层次结构。可以在代码中再进行细粒度的区分处理。对于需要再转成其他dto多层结构,可以考虑根据request中key和dto路径映射。比如PersonDTO下有年龄(age)、男女(sex)等属性,也有CompanyDTO(公司)。
那么request中的参数就写成age=15,sex=男,companyDTO.companyName=xxx,companyDTO.companyAddress=xxx。映射时,先截取再处理。
2、dto改成map
dto毕竟是需要在系统中特别编写,而实际处理时可能希望通过map去映射,如果通过map则思路就要变成根据request的参数去赋值map了