1、前端控制器
2、OGNL/ VS:OGNL/ ValueStack
3、Action:动作
4、Result:结果
5、Interceptor:拦截器
6、Tags:标签库
一、Struts2 的工作流程
1、所有请求提交给 前端控制器。
2、根据配置信息确定要调用的 Action。
3、创建一个 ValueStack 对象(每个请求都有一个独立的 VS 对象)。
4、创建 Action 对象,把 Action 对象放到 VS 的栈顶,将 VS 对象存入到 request 中,存储的 key 为“ struts.valueStack”。
5、 控制器调用 Action 对象接收请求参数, 并在方法中根据输入属性算输出属性。
6、在调用 Action 之前或之后调用一系列 Interceptor。
7、根据 Action 返回的字符串确定 Result( 10 种类型)。
8、调用 Result 对象,将 VS 中的数据按照特定的格式输出。
9、很多情况下, Result 将转发到 JSP, JSP 页面用 Tags 取出数据并显示。
10、请求处理完后,将 ValueStack 对象和 Action 对象销毁。
二、OGNL技术
1、什么是OGNL
OGNL是 Object-Graph Navigation Language(对象图形导航语言)的缩写,它是一种功能强大的表达式语言。OGNL 可以让我们用非常简单的表达式访问对象层,它用一个独立的 lib 形式出现(封装于 ognl.jar 中),方便我们使用或构建自己的框架。
2、OGNL基本语法
OGNL引擎访问对象的格式: Ognl.getValue("OGNL 表达式",root 对象);//root 对象是 Ognl要操作的对象。
(1)访问基本类型属性:
System.out.println(Ognl.getValue("id", foo));
System.out.println(Ognl.getValue("name", foo));
(2) 访问数组属性:
System.out.println(Ognl.getValue("arry[0]", foo));
(3)访问集合 List 属性:
System.out.println(Ognl.getValue("list[1]", foo));
(4)访问集合 Map 属性:
System.out.println(Ognl.getValue("map.one", foo));
或 System.out.println(Ognl.getValue("map['two']", foo));
(5)基本运算:
System.out.println(Ognl.getValue("id+100", foo));
System.out.println(Ognl.getValue("\"What is \"+name", foo));
System.out.println(Ognl.getValue("\"name: \" + name + \" id: \" +id",foo));
System.out.println(Ognl.getValue("id > 150", foo));
(6)调用方法:
System.out.println(Ognl.getValue("name.toUpperCase()", foo));
System.out.println(Ognl.getValue("list.size()", foo));
(7)调用静态方法:调用静态方法的格式: @类名@方法名
System.out.println(Ognl.getValue("@java.util.Arrays@toString(arry)",foo));
(8)创建的 List 对象:
Object obj = Ognl.getValue("{1,2,3,4,5}", null);//这种方法更方便地临时创建一个 List 对象
System.out.println(obj.getClass().getName());//java.util.ArrayList
System.out.println(obj);//[1,2,3,4,5]
(9)创建的 Map 对象:
obj = Ognl.getValue("#{1:'java',2:'javajava',3:'javajavajava'}", null);//注意:“ #”号不能丢
System.out.println(obj.getClass().getName());//java.util.LinkedHashMap
System.out.println(obj);
3、OGNL体系结构
4、Struts2标签的使用
Struts2 的很多标记就是通过访问 ValueStack 获得数据的。使用前要在页面中要引入: <%@taglib uri="/struts-tags" prefix="s"%>, prefix:表示前缀
(1)通过 OGNL 从 ValueStack 取数据,并且显示。
<s:property value ="ognl 表达式"/>
(2)省略 value,取出 ValueStack 的栈顶( 默认从栈顶取值)。
<s:property />
(3)通过 OGNL 从 ValueStack 取出集合,依次将集合中的对象置于栈顶,在循环中,ValueStack 栈顶即为要显示的数据。
<s:iterator value="ognl 表达式指定的集合名">
<s:property value="name" /><!--将每个对象中 name 属性的值输出-->
</s:iterator>
将 OGNL 指定的集合属性循环迭代,在迭代中,会将当前元素压入 root 栈顶位置,因此使用“ 属性”格式的 OGNL,此时定位的是元素属性,而不是 Action 属性。 Iterator 循环结束后, Action 对象又恢复成栈顶。与 JSTL 标签中的 forEach 类型,也有相同的属性。其中var、 status 属性指定的变量存储在 context 区域,需要使用“ #变量”访问。
(4)if-else 判断标签
<s:if test=“ ognl 判断表达式” >满足条件时</s:if> <s:else>不满足条件时</s:else>
(5) 可在 value 属性中做字符串拼接
<s:property value="name + 'java'" />
(6) 可调用方法
<s:property value="arry[1].toUpperCase()" />
(7) 其他详细用法, 我们在 Struts2 标签中直接写 OGNL表达式( 字符串)即可
5、Struts2对 EL 表达式的支持
EL 表达式默认是访问 page、 request、 session、 application 范围的数据。在 Struts2 中也可以使用 EL 表达式访问 Action 对象的属性。访问顺序是 page、 request、root 栈( action)、 session、 application。当 EL 从 request 中获取不到信息后, 会去 ValueStack对象的 root 栈获取,原因是: Struts2 重写了 HttpServletRequest 的方法。
二、Action
1、Action 属性注入
在<action>配置中,为 Action 组件的属性指定一个初始值,该值在创建 Action 对象时注入,可以将一些变化的参数值,利用该方法指定。例如: pageSize、 password 密码、 dir 存储路径等。
例:
public class ParamAction{
private String param1;
private int param2;
//各 自get/set略
}
<action name="param" class="com.audTest.ParamAction">
<param name="param1 ">root</param>
<param name="param2">root</param>
</action>
2、在 Action 中访问 Session 和 Application(1)利用 ActionContext,返回 Map 类型
ActionContext context=ActionContext.getContext();
Map<String, Object> session=context.getSession();
session.put("username", "hang");
Map<String, Object> request=(Map)context.get("request");
Map<String, Object> app=context.getApplication();
注意: 不推荐使用 ActionContext 访问 Session 的方式,因为这种方式的“ 侵入性” 较强。 ActionContext 是 Struts2 的 API,如果使用其他框架代替目前的Struts2 框架,而我们实际项目中的 Action 的数量非常庞大,每个类都修改的话,会非常麻烦。 推荐使用实现 SessionAware 接口的方式。(2)ServletActionContext 返回的是 Servlet 使用类型
HttpServletRequest httpreq=ServletActionContext.getRequest();
HttpSession httpsession=httpreq.getSession();
ServletContext httpapp=ServletActionContext.getServletContext();
(3) 在 Action 中实现 Aware 接口,由框架底层将对象注入给 Action 变量RequestAware: Map 类型的 request
SessionAware: Map 类型的 session
ApplicationAware: Map 类型的 application
ServletRequestAware: http 类型的 request
ServletResponseAware: http 类型的 response
ServletContextAware: http 类型的 application
3、使用通配符配置 Action
(1) *_*:代表名称中有“ _”下划线的所有 Action。
(2) 例:
<action name="*_*_*" class="com.audTest.{1}Action" method="{2}">
<result name="success">/WEB-INF/jsp/{3}.jsp</result>
</action>
①{1}Action:表示 name 属性中第一个“ *”星号匹配的字符串。
②method="{2}":表示方法名为 name 属性中第二个“ *”星号匹配的字符串。
③{3}.jsp:表示显示的页面为 name 属性中第三个“*”星号匹配的字符串。
4、Struts2中Action 的设计经验
(1)配置文件的拆分。
(2)控制好 Action 类的粒度(不能太多或就 1 个)。
(3) Action 和 Servlet API 的耦合度要低,如非要用底层 API,实现 XXXAware 接口,可以封装拦截器,如:sessionAware。
(4)适当用通配符。
(5)根据输入算输出。
三、Result
Result 组件是一个类,职责是生成视图。该类必须实现 Result 接口,并且将约定的 execute方法实现,在该方法中编写了生成响应输出的逻辑代码。视图可以是多种多样的(比如 JSP、 JSON、报表、 freeMarker 等),这些视图都可以由Result 负责。
1、Result 注册配置
<package>
<result-types>
<result-type name="类型名" class="实现类"
</result-type>
</package>
2、Result 组件利用 <result>元素的 type 属性指定 result 类型<action>
<result type="result 类型">
//result 参数指定
</result>
</action>
3、常见的 Result 组件类型
(1) dispatcher(默认):以请求转发方式调用一个 JSP,生成响应视图。
(2) redirect:以重定向方式调用一个 JSP,生成响应视图。
(3) redirectAction:以重定向方式调用一个 Action。
参数: ①actionName: 要定向到的 Action 的 name 值。
②namespace:要定向到的 Action 所在包的命名空间的名字。
(4) chain:以请求转发方式调用一个 Action。
(5)stream:以字节流方式响应,将 Action 中指定的一个 InputStream 类型的属性以字节
流方式输出。
参数: ①inputName: OGNL 表达式,表示要输出的一个输入流对象(要输出数据的
来源)。 ②contentType:用于设置响应中 contentType。
(6) json:以 json 字符串响应,将 Action 中指定的属性,拼成一个 json 字符串输出。
参数: root: OGNL 表达式,要做成 JSON 字符串的对象
四、拦截器
1、拦截器的作用
拦截器适合封装一些共通处理,便于重复利用。例如:请求参数给 Action 属性,日志的记录,权限检查,事务处理等。拦截器是通过配置方式调用,因为使用方法比较灵活,便于维护和扩展。
2、拦截器的常用方法
(1) 如果不调用下面的两种方法, 那么就不会调用 Action,也不会调用后面的 Interceptor,拦截器中的 return 的 String决定了最后的 Result。
(2) arg0.invoke();调用 Action,也包括 Result,拦截器中的 return 的内容无效。
(3) arg0.invokeActionOnly();调用 Action,不包括后面的 Interceptor 和 Result,拦截器中的return 的 String 决定了最后的 Result。
(4)在拦截器中, 如何调用底层 API:
①可调用 ActionInvocation 对象的 getStack 方法获取 VS。
②可调用 ServletActionContext 的静态方法获取 Servlet API,如 getResponse 获取response。
3、自定义拦截器
(1)编写拦截器组件,组件类实现 Interceptor 接口 ,实现约定的 interceptor 方法。在该方法中添加自定义的共通处理
pubic class DemoInterceptor implments Interceptor{
public String interceptor(ActionInvocation ai){
//拦截器前部分处理
ai.invoke();//执行 action 和 result,或 ai.invokeActionOnly()
//拦截器后续处理
}
}
(2)将拦截器注册给 Struts2 框架,在 struts.xml 中注册(添加该拦截器的声明)<package>
<interceptors>
<interceptor name="名称" class="实现类" />
//...其他 interceptor
</interceptors>
</package>
(3)使用拦截器组件(添加该拦截器的引用)①方式一:定义默认拦截器
<default-interceptor-ref name="拦截器名" />
默认(不写)情况下,一个 Action 在执行时,会默认调用 defaultStack拦截器栈(拦截器栈相当于一组拦截器的组合。 在引用时,就把它当成一个拦截器。)。
②方式二:显式指定调用哪个拦截器
<action>
<interceptor-ref name="拦截器名"/>
//...可以写多个
</action>
当指定 Action 调用的拦截器后,默认的 defaultStack 将不再执行
4、fileUpload 拦截器原理
该拦截器首先会调用 commons-file-upload.jar 组件,将客户端上传的文件保存到服务器临时目录下,之后将临时目录下的文件对象给 Action 属性赋值。当 Action 和 Result 调用完毕之后,清除临时目录下的文件。因此在 Action 业务方法中,需要做文件复制,将临时文件转移到目标目录中。
五、Struts2标签
Struts2标签库提供了主题、模板支持,极大地简化了视图页面的编写。由于struts2的主题、模板都提供了很好的扩展性,实现了更好的代码复用。Struts2允许在页面中使用自定义组件,满足项目中页面显示复杂、多变的需求。struts2标签库的标签不依赖于任何表现层技术,也就是说strtus2提供了大部分标签,可以在各种表现技术中使用,包括最常用的jsp页面。
1、标签属性
(1) label: input 类型的按钮,不能用 label 设置按钮上的文本,只能用 value。在主题 theme为 simple 下, label、 tooltip 不起作用。
(2) labelposition
(3) required
(4) tooltip
(5) tooltipIconPath
(6) cssClass: html 中的 class
(7) cssStyle: html 中的 style
(8) value:用于获取值
(9) name:在 Struts2 标签中,用于提交值或获取值;在普通 HTML 标签中用于提交值
注意:
(1)Struts2 中的标签 name 属性有双重作用,获取值,提交值,前提是获取和提交值的名字一致,如果不一致则需要分别写 value 属性和 name 属性。
(2)HTML 标签中如若有回显效果,也要提交值,则必须写 value 和 name 属性。