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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解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中对应的属性

来看代码:

Java代码  技术分享
  1. //com.opensymphony.xwork2.interceptor.ParametersInterceptor.java  
  2. //主要代码如下:  
  3. //...  
  4.   
  5.     @Override  
  6.     public String doIntercept(ActionInvocation invocation) throws Exception {  
  7.         Object action = invocation.getAction();  
  8.         if (!(action instanceof NoParameters)) {  
  9.             ActionContext ac = invocation.getInvocationContext();  
  10.             final Map<String, Object> parameters = retrieveParameters(ac);  
  11.   
  12.             if (LOG.isDebugEnabled()) {  
  13.                 LOG.debug("Setting params " + getParameterLogMap(parameters));  
  14.             }  
  15.   
  16.             if (parameters != null) {  
  17.                 Map<String, Object> contextMap = ac.getContextMap();  
  18.                 try {  
  19.                     ReflectionContextState.setCreatingNullObjects(contextMap, true);  
  20.                     ReflectionContextState.setDenyMethodExecution(contextMap, true);  
  21.                     ReflectionContextState.setReportingConversionErrors(contextMap, true);  
  22.   
  23.                     ValueStack stack = ac.getValueStack();  
  24.                     setParameters(action, stack, parameters);  
  25.                 } finally {  
  26.                     ReflectionContextState.setCreatingNullObjects(contextMap, false);  
  27.                     ReflectionContextState.setDenyMethodExecution(contextMap, false);  
  28.                     ReflectionContextState.setReportingConversionErrors(contextMap, false);  
  29.                 }  
  30.             }  
  31.         }  
  32.         return invocation.invoke();  
  33.     }  
  34.   
  35.     protected void setParameters(Object action, ValueStack stack, final Map<String, Object> parameters) {  
  36.         ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware)  
  37.                 ? (ParameterNameAware) action : null;  
  38.   
  39.         Map<String, Object> params;  
  40.         Map<String, Object> acceptableParameters;  
  41.         if (ordered) {  
  42.             params = new TreeMap<String, Object>(getOrderedComparator());  
  43.             acceptableParameters = new TreeMap<String, Object>(getOrderedComparator());  
  44.             params.putAll(parameters);  
  45.         } else {  
  46.             params = new TreeMap<String, Object>(parameters);  
  47.             acceptableParameters = new TreeMap<String, Object>();  
  48.         }  
  49.   
  50.         for (Map.Entry<String, Object> entry : params.entrySet()) {  
  51.             String name = entry.getKey();  
  52.   
  53.             boolean acceptableName = acceptableName(name)  
  54.                     && (parameterNameAware == null  
  55.                     || parameterNameAware.acceptableParameterName(name));  
  56.   
  57.             if (acceptableName) {  
  58.                 acceptableParameters.put(name, entry.getValue());  
  59.             }  
  60.         }  
  61.   
  62.         ValueStack newStack = valueStackFactory.createValueStack(stack);  
  63.         boolean clearableStack = newStack instanceof ClearableValueStack;  
  64.         if (clearableStack) {  
  65.             //if the stack‘s context can be cleared, do that to prevent OGNL  
  66.             //from having access to objects in the stack, see XW-641  
  67.             ((ClearableValueStack)newStack).clearContextValues();  
  68.             Map<String, Object> context = newStack.getContext();  
  69.             ReflectionContextState.setCreatingNullObjects(context, true);  
  70.             ReflectionContextState.setDenyMethodExecution(context, true);  
  71.             ReflectionContextState.setReportingConversionErrors(context, true);  
  72.   
  73.             //keep locale from original context  
  74.             context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));  
  75.         }  
  76.   
  77.         boolean memberAccessStack = newStack instanceof MemberAccessValueStack;  
  78.         if (memberAccessStack) {  
  79.             //block or allow access to properties  
  80.             //see WW-2761 for more details  
  81.             MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;  
  82.             accessValueStack.setAcceptProperties(acceptParams);  
  83.             accessValueStack.setExcludeProperties(excludeParams);  
  84.         }  
  85.   
  86.         for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {  
  87.             String name = entry.getKey();  
  88.             Object value = entry.getValue();  
  89.             try {  
  90.                 newStack.setParameter(name, value);  
  91.             } catch (RuntimeException e) {  
  92.                 if (devMode) {  
  93.                     String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{  
  94.                              "Unexpected Exception caught setting ‘" + name + "‘ on ‘" + action.getClass() + ": " + e.getMessage()  
  95.                     });  
  96.                     LOG.error(developerNotification);  
  97.                     if (action instanceof ValidationAware) {  
  98.                         ((ValidationAware) action).addActionMessage(developerNotification);  
  99.                     }  
  100.                 }  
  101.             }  
  102.         }  
  103.   
  104.         if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))  
  105.             stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));  
  106.   
  107.         addParametersToContext(ActionContext.getContext(), acceptableParameters);  
  108.     }  
  109. //...  

 

上面的代码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类:

Java代码  技术分享
  1. package com.liany.demo.pubs.org.employee.action;  
  2.   
  3. import java.util.List;  
  4.   
  5. import com.opensymphony.xwork2.ActionSupport;  
  6.   
  7. import com.liany.demo.pubs.org.employee.model.Employee;  
  8. import com.liany.demo.pubs.org.employee.service.EmployeeService;  
  9.   
  10. public class EmployeeAction extends ActionSupport{  
  11.       
  12.     private EmployeeService employeeService;  
  13.     private List list;   
  14.     private Employee employee = new Employee();    
  15.       
  16.       
  17.     public StringReader getStringReader() {  
  18.         StringReader is = null;  
  19.         try {  
  20.             is = new StringReader(xmlBuf.toString());  
  21.         } catch (Exception e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.         return is;  
  25.     }  
  26.       
  27.     public Employee getEmployee() {  
  28.         return employee;  
  29.     }  
  30.   
  31.     public void setEmployee(Employee employee) {  
  32.         this.employee = employee;  
  33.     }   
  34.   
  35.     public List getList() {  
  36.         return list;  
  37.     }  
  38.   
  39.     public void setEmployeeService(EmployeeService employeeService) {  
  40.         this.employeeService = employeeService;  
  41.     }  
  42.       
  43.     public String execute(){  
  44.         //列表  
  45.         list = this.employeeService.getEmployees();  
  46.           
  47.         return "list";  
  48.     }  
  49.       
  50.     public String view(){  
  51.         employee = this.employeeService.getEmployeeById(employee.getId());  
  52.           
  53.         return "view";  
  54.     }  
  55.       
  56.     public String edit(){  
  57.         if(employee.getId()!=null){  
  58.             //修改  
  59.             employee = this.employeeService.getEmployeeById(employee.getId());  
  60.         }else{  
  61.             //新增  
  62.             employee.setId(null);  
  63.         }  
  64.           
  65.         return "input";  
  66.     }  
  67.       
  68.     public String save(){  
  69.         this.employeeService.saveEmployee(employee);  
  70.           
  71.         return "repage";  
  72.     }  
  73.       
  74.     public String delete(){  
  75.         this.employeeService.deleteEmployeeById(employee.getId());  
  76.           
  77.         return "repage";  
  78.     }  
  79. }  

 

上面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

以上是关于理解Struts2的Action中的setter方法是怎么工作的的主要内容,如果未能解决你的问题,请参考以下文章

struts2中的Action接口和Actionsupport接口各有啥作用

struts2 的action 怎么向页面传值?

Struts2 中的值栈的理解

Struts2中param的作用

Struts2速记手册

struts请求参数注入的三种方式