一、Stuts的元素
1 web.xml
任何一个web应用程序都是基于请求响应模式进行构建的,所以无论采用哪种MVC框架,都离不开web.xml文件的配置.换句话说,web.xml并不是Struts2框架特有的文件,只有在Web应用中配置了web.xml文件,MVC框架才能真正地与Web应用融合起来.因此,web.xml文件是所有JavaWeb应用程序都需要的核心文件.
Struts2框架需要在web.xml中配置其核心控制器—StrutsPrepareAndExecuteFilter,用于对框架进行初始化,以及处理所有的请求.
StrutsPrepareAndExecuteFilter可以包含一些初始化参数,经常使用的如config——要加载的XML形式的配置文件列表(多个配置文件以逗号分隔,) 如果没有设置这个参数,Struts2框架将默认加载struts-default.xml,struts-plugin.xml和struts.xml.
StrutsPrepareAndExecuteFilter作为一个Filter在Web应用中运行,它负责拦截所有的用户请求,当用户请求到达时,该Filter会过滤用户请求.如果用户请求以action结尾,该请求将被传递到Struts2框架进行处理.
2 Action
实际上,在MVC框架中,控制器是由两个部分组成,分别如下:
- 核心过滤器(Filter):用于拦截用户请求,对请求进行处理
- 业务控制器(Action):调用相应的Model类实现业务处理,返回结果.
对于开发人员来说,使用Struts2框架,主要的编码工作就是编写Action类,而自定义的Action需要实现com.opensymphony.xwork2.Action接口和继承com.opensymphony.xwork2.ActionSupport类,Struts2并不要求编写的Action类一定要实现Action接口,可以编写一个普通的Java类作为Action类,只要该类含有一个返回字符串的无参的public方法即可.
在实际开发中,Action类通常都继承自Struts2提供的com.opensymphony.xwork2.ActionSupport类,以便简化开发.
3 Result
result元素的作用是实现结果视图的调用,并决定视图以哪种形式展现给客户端.简单地说,就是用来设定在Action处理结束后,系统下一步将要做什么.
Action类在处理完用户操作后,会返回一个处理结果.这个结果是一个简单字符串,框架根据这个字符串选择对应的Result,所以我们又将其称为逻辑视图名称.这个逻辑视图名由result元素的name属性来表示.result元素的值用来指定这个逻辑视图对应的物理视图资源的位置.需要特别指出的是,逻辑视图名称只有与物理视图资源联系在一起,才能发挥作用,所以必须要在配置文件中设置二者之间的对应关系.
通过对Struts2执行过程的分析,可以发现Struts2应用的整个过程都是按照请求/响应的过程执行的,如下图:
1) 当Web服务器接收到请求之后,将请求交由web.xml中配置的struts2框架的核心过滤器StrutsPrepareAndExecuteFilter.
2) 由StrutsPrepareAndExecuteFilter确定请求对应的Action(业务控制器)
3) 框架根据Action返回的结果字符串,由StrutsPrepareAndExecuteFilter选择对应的result,将结果呈现给用户.
二、 Struts2的配置文件 struts.xml
1. constant元素
constant元素用于配置常量,通过常量的配置,可以改变Struts2框架的一些行为,从而满足不同应用的需求.constant元素包含两个属性:其中name属性表示常量的名称,value表示常量的值.例如:
<constant name="struts.configuration.xml.reload" value="true" />
Struts2默认的常量配置在default.properties文件中,我们可以在struts.xml文件中进行修改:
常用的常量配置:
<!-- 常用的常量设置 --> <!--指定默认编码,作用于HttpServletRequest的setCharacterEncoding() 方法 --> <constant name="struts.i18n.encoding" value="UTF-8" /> <!--设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 --> <constant name="struts.serve.static.browserCache" value="false" /> <!--设置struts配置文件修改后,系统是否自动重新加载该文件,,默认为false(生产环境下使用) ,开发阶段最好打开 --> <constant name="struts.configuration.xml.reload" value="true" /> <!--开发模式下使用,这样可以打印详细的错误信息 --> <constant name="struts.devMode" value="true" /> <!--默认的视图主题, 该Struts标签的样式 --> <constant name="struts.ui.theme" value="simple" /> <!--与spring整合时,指定由spring负责action对象的创建 --> <constant name="struts.objectFactory" value="spring" /> <!--该属性设置Struts2是否支持动态方法调用,该属性的默认值是true,如果需要关闭动态方法的调用,属性为false --> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <!--上传文件的大小限制 --> <constant name="struts.multipart.maxSize" value="10240000" /> <!-- 指定需要Struts2处理的请求后缀: 设置常量“struts.action.extension”进行修改, 多个后缀名,用逗号隔开 --> <constant name="struts.action.extension" value="do,action" />
2 package元素
<package name="default" namespace="/" extends="struts-default">
Struts2框架会把action,result等元素组织在一个名为package(包)的逻辑单元中.Struts的package很想java中的包,但与java包不同的是,Struts2中的包可以”继承”已经定义好的包,
我们自定义的package要直接或间接的继承 struts-default的包.
package的属性:
- name 属性:为必需并且唯一的,用来指定包的名称,便于被其他包继承
- extends属性类似Java的extends关键字,指定继承那个包
- namespace是一个可选属性,该属性定义该包中action的命名空间,如果没有设置该属性,则action被放入默认命名空间中.Struts2空间使用action的名称和它所在包的命名空间来标识一个action,默认的命名空间用””表示,也可以使用”/”定义一个根命名空间,注意两者是有区别的.如果请求Web应用程序路径下的action,则框架在根命名空间中查找对应的action.
3.action元素
对于Struts2应用的开发者而言,Action才是应用的核心,开发者需要提供大量的Action类,并在struts.xml文件中配置Action.Action主要三个作用:
1.action最重要的作用是为给定的请求封装需要做的实际工作(调用特定的业务处理类);
2.为数据的转移提供场所
3.Action必须帮助框架决定由哪个结果呈现请求响应
Action元素的属性:
1.method属性
在之前的程序中,每实现一个功能都会去创建一个Action.并且完成相应的方法,那么是否可以在同一个Action中实现不同的功能呢?答案是可以的,那我们就需要使用到
action元素的method属性实现在同一个Action中处理不同的请求。
package org.suke.struts.web.action; import com.opensymphony.xwork2.ActionSupport; /** * 一个Action可以包含多个请求处理方法 * @author Administrator * */ public class UserAction extends ActionSupport { /** * 处理登录请求的方法 * @return */ public String login(){ return SUCCESS; } /** * 处理注册请求的方法 * @return */ public String register(){ return SUCCESS; } }
struts.xml的配置
在上面的配置文件中,可以看到配置文件中分别定义了两个action元素,每个action元素的name属性是不同的,但是其指向的实现类的引用却是相同的.那么,Struts2在接收请求后通过method属性,
确定该执行同一个Action中哪一个方法,也就是说,如果用户请求的是login.action,那么久调用UserAction的login()方法,如果是register.action,则调用register()方法。
2.动态调用DMI:
不使用method实现统一.
<action name="userAction" class="org.suke.struts.web.action.UserAction"> <result name="success">/index.jsp</result> </action>
然后再在index.jsp中定义如下链接:
<a href="<%=path %>/userAction!login.action">登录</a><br> <a href="<%=path %>/userAction!register.action ">注册</a><br>
注意查看上面的链接地址,它们都是针对UserAction,然后再加地上“!+UserAction中相应的方法名”,最后再写上.action即可以访问到统一页面index.jsp。这样做虽然能减少页面,
但是由于它们实质用到的是同一个Action,所以这就意味着我们要使用的拦截器相同,相同的跳转result。实际中这种方式很少使用,在此略作了解。
如果不想使用动态方法调用,我们可以通过常量来关闭,即在struts.xml中增加如下配置:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
3 .Action的通配符的使用
在配置<action../>元素时,需要指定name,class和method属性,其中name属性支持通配符,然后可以在method属性使用表达式,通配符用”* ”表示.表示匹配任意的字符串。
<action name="*User" class="org.suke.struts.web.action.UserAction" method="{1}"> <result name="success">/{1}.jsp</result> </action>
在action元素的name属性中使用星号,允许这个Action匹配所有以User结束的URI,如loginUser.action.配置该action元素时,还指定了method属性,该属性使用一个表达式{1},
该表达式的值就是name属性值中第一个*的值.例如,当请求为loginUser.action时,通配符匹配的是login,那么这个值将替换{1},最终请求loginUser.action,将由UserAction的login()方法执行。
4. 配置默认的Action
如果请求一个不存在的Action,那么结果将会在页面上呈现HTTP404错误,为了解决这个问题.Struts2框架允许指定一个默认的Action,即如果没有一个Action匹配请求,那么默认的Action被执行.
<!-- 使用默认的action --> <default-action-ref name="defaultAction"></default-action-ref> <!-- 配置默认的action --> <action name="defaultAction"> <result>/error.jsp</result> </action>
5. Result的配置
1.Result的常用结果类型
以上对type类型作简要的说明,下面来看实例:当一个Action处理后要返回的Result是另一个Action时,作如何配置,关键就是配置type类型。
步骤一:建立两个Action:TestAction、Test2Action
步骤二:web.xml配置省略。struts.xml主要配置内容如下:
<package name="resultTest" extends="struts-default"> <action name="test" class="com.asm.TestAction"> <result name="success" type="chain"> <param name="actionName">test2</param> </result> </action> <action name="test2" class="com.asm.Test2Action"> <result name="success">/test2Suc.jsp</result> </action> </package>
说明:在名为“test”的action中,我们配置result元素的type类型值为chain,意为将继续把Action传递到下一个名为test2的Action中去,在test2.action中会把页面转向到test2Suc.jsp中去。
在type类型为chain时,它的param有4个值可配,除了这里用到的name=”actionName”外(必须配置,否则报错),还有name=namespace|method|skipActions。
其中namespace指定要转向到action的名字空间,由于此处是转到Action位于同一个namespace下,而namesapace的默认值the current namespace,
所以可以省略不写(需要说明的是如果要跳到别的名称空间的action中去,除了使用namespace指定外,还可以用:/要跳去action所在名称空间的值/要跳去的action的name值)。
Method用于指定转向到一个目标action所调用的方法,默认是调用下一个action的execute方法,所以此处仍可以省略。
SkipActions是一个可选的属性,一般不用。具体可以参看chain所对应类的api帮助。
在本实例中,我们还在TestAction中设定一个username字段,并在execute方法执行为它赋了值,并在test2Suc.jsp中引用了此值。其实这种做法在web开发中还是很有用处,
比如可以代替隐藏域。需要注意的是之所以在action的传递中能把设定的这个值保存下去,主要是因为转向都是服务器跳转。
如果我们跳转时采取了客户端跳转,也就是重定向,比如在test2 action的result中指定type类型为redirect,要想传递参数可以在result指向的jsp页面中附加参数即可,我们可以在test2 action的result中写成:
<result name="success" type="redirect">/test2Suc.jsp?username=${username}</result>
随后在test2Suc.jsp页面中引用时会出现三个问题:
1.EL表达式引用失效,(EL表达式应该使用${param.username}形式)。我们也可以使用<%=request.getParameter("username")%>获取参数值。
2.由于在前面的TestAction中设定的值为中文,而附加到这里的uri请求的参数后面时会出现乱码问题。(可以使用URI编码再解码解决此问题)
3.值栈取值失效:因为每一次request共享同一个值栈,所以服务器端的forward跳转也是能共享同一值栈得。但是着当test action执行后把请求交由test2 action时,
test2 action采取的是redirect重定向到test2Suc.jsp页面,这时其实就是重发的一次request,所以在test action保存的值栈内容全部失效。这也就是为什么我们要附加参数的原因。
而参数是保存在actionContext中,所以采用了#的方式来取出值。图示说明:
2. 全局结果
之前我们配置的结果都在action元素的内部,这些结果不能被其他的Action使用,在一些情况下,多个Action可能需要访问同一个结果,这时需要配置全局结果来满足多个Action共享一个结果.
<!-- 配置全局的Result --> <global-results> <result name="success">/success.jsp</result> <result name="input" type="redirect">/login.jsp</result> </global-results>
全局结果同样也使用result元素配置,与action配置result元素的区别在于:全局结果需要在global-results元素中嵌套result元素,如果在Action中的result元素名称与全局结果的名称相同时,
Action的result元素将会覆盖全局result结果.也就是说:Action处理用户请求结束后,会先在本Action中的result结果中搜索域逻辑视图名队员的结果,
只有当在Action中无法匹配与逻辑视图名对应的结果时,才会去寻找全局结果并执行,