`
xitong
  • 浏览: 6188863 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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 object
public 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;
    }

    @Override
	public 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;

		@Override
		public 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 {
	@Override
	public 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的思想比较优秀啊。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics