理解Struts2的Action中的setter方法是怎么工作的

接触过webwork和Struts2的同行都应该知道,

提交表单的时候,只要Action中的属性有setter 方法,这些表单数据就可以正确赋值到Action中属性里;
另外对于Spring配置文件中声明的bean,也可以在Action中声明setter 方法将其注入到Action实例中。
那么现在要研究:这些是怎么工作的呢?


(1)提交表单时的参数
在struts2-core-2.3.1.2.jar压缩包内的struts-default.xml配置文件中有这个配置:
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
这个拦截器是负责解析请求中的URL参数,并赋值给action中对应的属性

来看代码:

//com.opensymphony.xwork2.interceptor.ParametersInterceptor.java
//主要代码如下:
//...

    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        if (!(action instanceof NoParameters)) {
            ActionContext ac = invocation.getInvocationContext();
            final Map<String, Object> parameters = retrieveParameters(ac);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Setting params " + getParameterLogMap(parameters));
            }

            if (parameters != null) {
                Map<String, Object> contextMap = ac.getContextMap();
                try {
                    ReflectionContextState.setCreatingNullObjects(contextMap, true);
                    ReflectionContextState.setDenyMethodExecution(contextMap, true);
                    ReflectionContextState.setReportingConversionErrors(contextMap, true);

                    ValueStack stack = ac.getValueStack();
                    setParameters(action, stack, parameters);
                } finally {
                    ReflectionContextState.setCreatingNullObjects(contextMap, false);
                    ReflectionContextState.setDenyMethodExecution(contextMap, false);
                    ReflectionContextState.setReportingConversionErrors(contextMap, false);
                }
            }
        }
        return invocation.invoke();
    }

    protected void setParameters(Object action, ValueStack stack, final Map<String, Object> parameters) {
        ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware)
                ? (ParameterNameAware) action : null;

        Map<String, Object> params;
        Map<String, Object> acceptableParameters;
        if (ordered) {
            params = new TreeMap<String, Object>(getOrderedComparator());
            acceptableParameters = new TreeMap<String, Object>(getOrderedComparator());
            params.putAll(parameters);
        } else {
            params = new TreeMap<String, Object>(parameters);
            acceptableParameters = new TreeMap<String, Object>();
        }

        for (Map.Entry<String, Object> entry : params.entrySet()) {
            String name = entry.getKey();

            boolean acceptableName = acceptableName(name)
                    && (parameterNameAware == null
                    || parameterNameAware.acceptableParameterName(name));

            if (acceptableName) {
                acceptableParameters.put(name, entry.getValue());
            }
        }

        ValueStack newStack = valueStackFactory.createValueStack(stack);
        boolean clearableStack = newStack instanceof ClearableValueStack;
        if (clearableStack) {
            //if the stack's context can be cleared, do that to prevent OGNL
            //from having access to objects in the stack, see XW-641
            ((ClearableValueStack)newStack).clearContextValues();
            Map<String, Object> context = newStack.getContext();
            ReflectionContextState.setCreatingNullObjects(context, true);
            ReflectionContextState.setDenyMethodExecution(context, true);
            ReflectionContextState.setReportingConversionErrors(context, true);

            //keep locale from original context
            context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));
        }

        boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
        if (memberAccessStack) {
            //block or allow access to properties
            //see WW-2761 for more details
            MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
            accessValueStack.setAcceptProperties(acceptParams);
            accessValueStack.setExcludeProperties(excludeParams);
        }

        for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {
            String name = entry.getKey();
            Object value = entry.getValue();
            try {
                newStack.setParameter(name, value);
            } catch (RuntimeException e) {
                if (devMode) {
                    String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
                             "Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + e.getMessage()
                    });
                    LOG.error(developerNotification);
                    if (action instanceof ValidationAware) {
                        ((ValidationAware) action).addActionMessage(developerNotification);
                    }
                }
            }
        }

        if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))
            stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));

        addParametersToContext(ActionContext.getContext(), acceptableParameters);
    }
//...

上面的代码ValueStack stack = ac.getValueStack();
表明,它是从当前Action上下文获取值栈(其实就类似一个全局Map集合,用来存储参数值或struts上下文全局变量),
然后由判断如果是当前action可以接受的参数(Action中有setter方法)就过滤出来,
调用这句“newStack.setParameter(name, value); ”来保存到值栈中,
保存到了值栈中其实action实例的属性就能拿到值了。
最后一句“addParametersToContext(ActionContext.getContext(), acceptableParameters);
表明它还把这些过滤出来的参数保存到了ActionContext上下文中,
这样,如果跳转的类型是forward(服务器内部重定向),
目标url中就会带上上次请求的url的所有有用的参数。

(2) Spring配置bean注入到Action中
来看一个简单的Action类:

package com.liany.demo.pubs.org.employee.action;

import java.util.List;

import com.opensymphony.xwork2.ActionSupport;

import com.liany.demo.pubs.org.employee.model.Employee;
import com.liany.demo.pubs.org.employee.service.EmployeeService;

public class EmployeeAction extends ActionSupport{
	
	private EmployeeService employeeService;
	private List list; 
	private Employee employee = new Employee();  
	
	
	public StringReader getStringReader() {
		StringReader is = null;
		try {
			is = new StringReader(xmlBuf.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return is;
	}
	
	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	} 

	public List getList() {
		return list;
	}

	public void setEmployeeService(EmployeeService employeeService) {
		this.employeeService = employeeService;
	}
	
	public String execute(){
		//列表
		list = this.employeeService.getEmployees();
		
		return "list";
	}
	
	public String view(){
		employee = this.employeeService.getEmployeeById(employee.getId());
		
		return "view";
	}
	
	public String edit(){
		if(employee.getId()!=null){
			//修改
			employee = this.employeeService.getEmployeeById(employee.getId());
		}else{
			//新增
			employee.setId(null);
		}
		
		return "input";
	}
	
	public String save(){
		this.employeeService.saveEmployee(employee);
		
		return "repage";
	}
	
	public String delete(){
		this.employeeService.deleteEmployeeById(employee.getId());
		
		return "repage";
	}
}

上面Action中的employeeService对象其实是在Spring配置文件中声明的bean,
代码中给它定义一个public的setEmployeeService()方法,这样就可以将bean实例注入到
Action中的实例中,这个功能是在Struts过虑器初始化的时候初始化了一个全局变量,
从而使得调用action时,从spring ioc容器中找到这个bean,再set给action对象。
配置文件是在struts.properties 文件中声明:
struts.objectFactory = spring
struts.objectFactory.spring.autoWire = name

好了先研究到这里, 有兴趣的朋友可以交流交流...

猜你喜欢

转载自modiliany.iteye.com/blog/1506541
今日推荐