Struts2通过拦截器反射批量获取参数

来源:互联网 发布:js 过滤空格 和换行符 编辑:程序博客网 时间:2024/05/20 11:19

Struts2通过拦截器反射批量获取参数

        之前用struts2弄一个erp项目,里面的表单字段数比较多。刚刚开始的时候耐心的一个一个去从request中拿,后来实在是受不了这种体力劳动了,遂上网寻找解决方案。总结起来,目前市面上比较常用的struts2批量获取参数的方法主要有以下几种。

1:通过添加action的属性设置get和set获取。

         这种方法是最常见的,确实挺高效,但我一直不愿意用。主要是感觉这种方法会导致整个action代码十分混乱,它们应该被集中起来放到一个对象(view object)中才对。并且通过这种方式,我们不能自己去控制它的默认值,得去深入了解struts2的脾气。

2:在action中设置一个对象获取。

         确实,action中可以构建一个对象,然后把属性塞到这个对象里,struts2框架会帮你把前端传递过来的数据填充进去。但最让人郁闷的是,页面变量名的书写格式必须是【对象名.属性名】。Oh,shit!为什么要弄这种耦合呢,本来页面就是要尽量的与后台分离,你总不能让前端写页面的时候先问下你打算用哪个类名吧。完全不理解struts2的作者,都到这一步了还急着去打dota,难道说是有什么别的目的,这个我就不懂了,有高人可以指点下。

3: ModelDriven接口

Struts2有一个ModelDriven接口。使用实例如下:

public class YouAction extends ActionSupport implements ModelDriven<YourBean> {    private YourBean sheep = new YourBean;    public YourBean getSheep() {        return sheep;    }    public void setSheep(YourBean sheep) {        this.sheep = sheep;    }    public String execute() throws Exception {        return SUCCESS;    }    @override    public YourBean getModel(){        return sheep;    }}

         为什么需要在两个地方指定bean呢,弄成一个的不行么?或者,完全不需要指定的不行么?其实我没用的原因主要是我没调试成功~~!不知道是不是我的bean继承了太多层还是我的action继承了太多层,总之我这样写,数据没给我放好,还请高手指点。话说这种叫模型驱动,方法1叫属性驱动,诶,不懂不懂……

在失望和绝望过后,我按照自己的设想,写了一套解决方案。

大概来说,就是用拦截器+反射把request的提交参数自动映射到我定义好的vo对象里(其实struts2也是这么干的),然后把参数验证也放到拦截器中去实现,这样每个action我只要提供返回boolean类型的验证函数就可以了。

AbstractVO.java,所有的vo对象(或者叫form、dto)都继承该类

import java.util.ArrayList;import java.util.List;//view objectpublic abstract class AbstractVO {    //get请求验证public boolean validGet(){return true;}    //post请求验证public boolean validPost(){return true;}    //get请求验证失败时的回调函数public void validGetFailRockback(){}    //post请求验证失败时的回调函数public void validPostFailRockback(){}    //验证消息存放列表,可以在验证失败时把原因传递出去private List<String> validMsg = new ArrayList<String>();public void addMsg(String msg){this.validMsg.add(msg);}public List<String> allMsg(){return this.validMsg;}//检查非空,这是常用的验证public boolean checkBlank(String...strings){for(String s:strings){if(s == null || s.equals("")){this.addMsg("某些数据不能为空");return false;}}return true;}    //其他通用的验证可以往下扩展}

DemoAction.java下面是action类

import java.util.Date;import com.opensymphony.xwork2.ActionSupport;public class DemoAction extends ActionSupport{private InnerForm form = new InnerForm();//必须先进行实例化    public InnerForm getForm() {//必须有get方法,拦截器需要,失败返回INPUT后页面状态保留也需要        return form;    }    @Overridepublic String execute() throws Exception {if(isGet()){            // 这里,你可以专注做你自己的事情了,数据获取和验证都已经在拦截器里帮你处理            // 你要做的就是从form对象里获取你自己需要的东西,可以使用apache的commos-BeabUtils帮你实现快速的bean属性转移,把form里面的东西分发给不同的model对象            return INPUT;}if(isPost()){            // 如上            return SUCCESS;}return ERROR;}    // 该form和该action是紧密相关的,可以定义为内部类,但要注意使用public修饰,这样失败才能通过get方法回传给前端public class InnerForm extends AbstractVO {    private String name;        private int age;@Overridepublic boolean validPost() {            boolean rt = true;rt = super.checkBlank(name);//调用公用的非空验证            if(age < 0 || age > 100){//一些个性化验证                super.addMsg("年龄范围不对");                rt = false;            }            return rt;}        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getAge() {            return age;        }        public void setAge(int age) {            this.age = age;        } }}

ParamInterceptor.java最关键的拦截器全部代码

import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.List;import javax.servlet.http.HttpServletRequest;import org.apache.struts2.StrutsStatics;import com.opensymphony.xwork2.Action;import com.opensymphony.xwork2.ActionSupport;import com.opensymphony.xwork2.ActionContext;import com.opensymphony.xwork2.ActionInvocation;import com.opensymphony.xwork2.interceptor.AbstractInterceptor;public class ParamInterceptor extends AbstractInterceptor {@Overridepublic String intercept(ActionInvocation invocation) throws Exception {ActionContext actionContext = invocation.getInvocationContext();        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);        ActionSupport action = (ActionSupport)invocation.getAction();String requestMethod = request.getMethod().toUpperCase();        Method[] methods = action.getClass().getMethods();//获取action的所有public方法        for(Method method:methods){            if(method.getName().startsWith("get")){//遍历action对象的get方法                if(AbstractVO.class.equals(method.getReturnType().getSuperclass())){//如果方法的返回类型是AbstractVO的直接子类(不支持多次继承,高手帮改进下)                    Object vo = null;                    try{                        vo = method.invoke(action, new Object[]{});//调用该方法希望能获取AbstractVO子类实例,这里要求action里定义的AbstractVO子类已经被实例化                    }catch(Exception o){}                    if(vo instanceof AbstractVO){//确保vo对象不为null并且是AbstractVO类型子类                        Method[] voMethods = vo.getClass().getMethods();                        for(Method voMethod:voMethods){                            if(voMethod.getName().startsWith("set")){//遍历vo对象的set方法                                Type[] methodParams = voMethod.getParameterTypes();                                Type[] methodGenericParams = voMethod.getGenericParameterTypes();                                String attributeName = voMethod.getName().substring(3, 4).toLowerCase() + voMethod.getName().substring(4, voMethod.getName().length());                                if(methodParams.length == 1){//如果set方法的参数个数是1                                Object defValue = null;                                boolean defLock = false;                                String[] oValues = request.getParameterMap().get(attributeName);                                    //下面开始设置给vo对象填充数据,应该有更简单的书写方式                                    try{                                        if(methodParams[0].equals(Integer.TYPE)){//如果set方法的参数类型是Integer或int类型                                            defValue = -1;//失败默认值                                            voMethod.invoke(vo, Integer.parseInt(oValues[oValues.length - 1]));//取最后一个参数,当get参数和post参数重名时优先使用post的参数                                                                                    }else if(methodParams[0].equals(String.class)){//如果set方法的参数类型是String类型                                            defValue = "";//失败默认值                                            voMethod.invoke(vo, oValues[oValues.length - 1]);                                        }else if(methodParams[0].equals(Long.TYPE)){//如果set方法的参数类型是long类型,可以用long类型保存价格,以和int类型分开(不懂还有没有别的解决方案,比如int类型是否可以定义别名)                                            //目的:把价格转化成分为单位的整数,比如12就是1200,12.3就是1230,12.03就是1203                                            defValue = -1l;//失败默认值                                            String source = oValues[oValues.length - 1];                                            String[] arr = source.split(".");                                            //……偷懒了                                            Long mylong = 1l;                                            voMethod.invoke(vo, mylong]);                                        }else if(methodParams[0].equals(Date.class)){                                        }else if(methodParams[0].equals(Boolean.TYPE)){                                        }else if(methodParams[0].equals(List.class)){//如果set方法的参数类型是List类型                                            Type rawType =  ((ParameterizedType)methodGenericParams[0]).getActualTypeArguments()[0];                                            if(rawType.equals(Integer.TYPE)){//如果List的泛型类型是Integer或int类型                                                List<Integer> rs = new ArrayList<Integer>();                                                defValue = rs;                                                for(String val:oValues){                                                rs.add(Integer.parseInt(val));                                                }                                                voMethod.invoke(vo, rs);                                            }else if(rawType.equals(String.class)){                                            }else if(rawType.equals(Date.class)){                                            }else if(rawType.equals(Boolean.TYPE)){                                            }else{}                                            //如果是array对象暂时还不懂怎么处理,高手帮忙                                        }else{                                        defLock = true;                                        }                                    }catch(Exception e){                                    if(!defLock){                                    try{                                        voMethod.invoke(vo, defValue);//尝试使用默认值                                        }catch(Exception s){                                        }                                    }                                    }                                }                            }                        }                //参数验证                boolean rt = true;                AbstractVO instance = ((AbstractVO)vo);                if(requestMethod.equals("GET")){                rt = instance.validGet();                if(!rt){                instance.validGetFailRockback();//失败回调                }                }                if(requestMethod.equals("POST")){                rt = instance.validPost();                if(!rt){                instance.validPostFailRockback();//失败回调                }                }                if(!rt){                for(String msg:instance.allMsg()){                                //如果验证失败了,这里可以处理失败消息,一般是放到session里传递给前端展示                }                return Action.INPUT;                }                    }                }            }        }return invocation.invoke();}}

Struts.xml,拦截器的配置还是得给些新手说说的,关键代码如下

        <interceptors>            <interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>            <interceptor name="context" class="com.sheep.ContextInterceptor"/>            <interceptor name="paramInit" class="com.sheep.ParamInterceptor"/>            <interceptor-stack name="basicStack">                <interceptor-ref name="basicStack"/>                <interceptor-ref name="staticParams"/>                <interceptor-ref name="context"/>                <interceptor-ref name="paramInit"/>            </interceptor-stack>        </interceptors>        <default-interceptor-ref name="basicStack"/>

另一个拦截器ContextInterceptor是做系统登录验证和权限验证什么用的,放它后面就行。

发现控制层还是spring的思想比较优秀啊。

原创粉丝点击