ActionContext
每个请求的处理都在一个独立的线程中,每个线程都有一个ActionContext对象,它包含了ValueStack和HttpServletRequest的东西。
Struts把所有的变量统一放在一个方便的地方,而不是将数据传来传去,这个东西就是ValueStack。它贯穿于整个处理流程,所以拦截器和视图层都可以操作ValueStack中的内容。一般通过OGNL间接操作,而不是直接操作。每个请求都会创建一个新的Action,因此不会有多线程冲突的问题。
动作
动作的作用就是封装业务逻辑、数据传输、决定响应视图。
动作可以看成是一个JavaBean,框架会将请求中的参数按照JavaBean规范填入到Action。之后在视图层可以读取Action中的数据。这就是数据传输的过程。为了简化问题,可以在Action中增加一个JavaBean类型的属性,这样视图层就可以将数据直接填到一个对象中,而不是填到动作中。另外可以让动作实现ModelDriven接口。这个接口的作用就是将所有的参数都定向到getModel这个方法中。getModel需要返回一个JavaBean。
public class TestAction { private Student student; // 返回一个JavaBean对象,这样就可以让参数填入对象 public Student getStudent() { return student; } }
文件的上传。文件的上传是通过FileUploadInterceptor实现的。文件字段可以看成三个请求参数。如果表单中的字段名为XXX,那么Action会接收到三个参数:
- XXX。File类型的数据,表示被上传的文件。
- XXXContentType。文件的内容类型。
- XXXFileName。文件储存在服务器上的文件名。
对于多个文件,只要将File改成File数组、String改成String数组即可。在一次请求结束之后,文件会被拦截器删除。
框架对动作的父类没有任何要求,只要实现execute方法即可。但是这样不能直接使用框架自带的便捷方法,因此可以引入ActionSupport基类。它的作用有数据验证、错误消息本地化等。
数据验证。DefaultWorkflowInterceptor会调用Action中的validate方法。开发者可以复写这个方法来检查数据是否正确。如果发现错误,可以调用addFieldError(field,message)这个方法添加错误。框架检测到错误之后会跳转到input页面让用户重新输入表单。ActionSupport还支持消息的本地化。首先要定义ActionName.properties,然后通过getText方法就可以获取。
下面介绍包的概念。首先打个比方,将Struts中的动作比作Java中的类。我们知道Java类一般都会放在一个包中。Struts也会将动作放在包中。包的定义在struts.xml文件中,下面是一个包的示例:
<package name="helloWorld" namespace="/my/path/helloWorld" extends="struts-default"> <action name="Hello"> <result>/my/hello.jsp</result> </action> <action name="World"> <result>/my/world.jsp</result> </action> </package>
默认namespace和根namespace的区别。默认namespace在定义的时候其实是一个空的字符串(不指定namespace即可),而根空间在定义的时候是“/”。举个例子,有一个请求http://localhost/hello/world/nice.action,框架首先会在/hello/world这个空间中找一个名为nice的动作,如果找不到,就会在默认命名空间中查找这个动作。