TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
9#
发表于 2015-06-02 12:45:13
|只看该作者
三、 Struts2+ E5 B; A+ e- B7 t# l) r* ~
1. Action名称的搜索顺序
6 D! v8 u; y+ F; C! k(1).获取请求路径的URL,例如URL是:http://server/struts2/path1/path2/path3/test.action
+ T9 X v7 [" w0 _& U D
% K/ G$ ^4 \2 s1 L0 D& q: L(2).首先寻找namespace为/path1/path2/path3的package,如果不存在这个package则执行步棸3,如果存在这个package,则在这个package中寻找名字为test的action,当在该package下寻找不到action时就会直接跑到默认namespace的package里面中寻找action(默认的命名空间为空字符串),如果在默认namespace的package里面还寻找不到该action,页面提示找不到action
2 L. p: o& Q* a( i/ p / e4 I1 b; K, r6 ]/ _" i
(3).寻找namespace为/path1/path2的package,如果不存在这个package,则转至步棸4,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认的namespace的package里面还寻找不到该action,页面提示找不到action
. u v0 }, c& [& _7 K; d0 Y( ~/ e$ S * r ?$ x; G: ^$ z! _" M4 `
(4).寻找namespace为/path1的package,如果不存在这个package则执行步棸5,如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action时就会直接跑到默认namespace的package里面去找名字为test的action,在默认namespace的package里面还寻找不到该action,页面提示找不到action) X; _# Y0 c4 H. [* e
, x6 E7 v& i/ f; \3 S4 m(5).寻找namespace为"/"的package,如果存在这个package,则在这个package中寻找名字为test的action,当在package中寻找不到action或者不存在这个package时,都会去默认namespace的package里面寻找action,如果还是找不到,页面提示找不到action5 ?: x+ ]9 G0 f% @( `* f6 m6 c$ _
' F7 x- p# o. w* s7 h(6).在struts2中默认的处理后缀是.action,不加后缀也可以. I) t! W" e# @6 _2 D7 H
" u' b8 S, l8 H( e- |
2. Action配置的各项默认值, `# K1 X' m7 z* O
(1).struts1中:<actionpath="/control/employee/addUI"forward="/WEB-INF/page/employeeAdd.jsp"/>实现请求转发,action将请求转发给视图jsp+ s r2 u2 z* W4 f, v
9 r9 p! x* I+ Z. V9 n
(2).在struts2中,<actionname="addUI"><result>/WEB-INF/page/employeeAdd.jsp</result></action>,不需要设置addUI的类路径class属性了
- h' C0 V1 x1 A* y# e
. r: X' {! D0 c! ^, w(3).Action配置中的各项默认值:* y$ x* d) n3 ^$ w- t& {# k
<package name="itcastnamespace="/test" extends="struts-default">0 s% D, P+ j# K; l7 t
<action name="hellowrold"class="cn.itcast.action.HelloWorldAction"method="execute">3 [6 ^: }+ o7 r- C B: S
<result name="success">/WEB-INF/page/hello.jsp</result>
4 ?5 N* R7 i( _6 h</action>
' M- ?5 X! [' p5 l) |</package> O q8 Z2 z0 y1 C0 b" f. T2 L. v5 J
如果没有为action指定class,默认是ActionSupport,可以查看ActionSupport的源代码,首先交给ActionSupport类处理.
" I; ^. V2 X$ z) R; k8 [( a5 Z5 N1 I如果没有为action指定method,默认执行action中的execute()方法,这个方法的返回值为"success";/ [+ t( i" l" c8 W
: g' t. I! L3 R+ ?4 \8 Y
(4).如果没有指定result的name属性,默认值为success,正好和execute方法的返回值相同,所以可以实现视图的转发* S8 h2 C( A' d2 Q
9 f' D$ r2 W9 ?0 E9 v$ k8 x3.OGNL表达式" T* M: U* R8 d* C& d" j
(1).OGNL表达式:OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目,struts2框架使用OGNL作为默认的表达式语言: Y; O5 S5 ]. E9 B- ^
第一:相对EL表达式,它提供了平时我们需要的一些功能,如:
1 P' X* u& |/ ~( @. d$ i5 |支持对象方法的调用,如:xxx.sayHello();: H& V9 N5 A! J8 N" G
第二:支持类静态方法调用和值访问,表达式的格式为@[类全名 (包括包路径) ]@[方法名|值名],例如:@java.lang.String@format('foo %s,'bar')或@cn.itcast.Constant@APP_NAME
8 N( e3 J5 C( |2 _第三:操作集合对象' \9 v; |* M. A2 d8 ]
, O- ~+ a8 E) G* K% g(2).OGNL有一个上下文(Context)概念,说白了上下文就是一个MAP结构,他实现了java.utils.Map接口,在struts2中上下文(context)的实现为ActionContext
, g% D, y0 ?, T5 G1 j0 A: p 9 ^. }, V% ?- x7 E. b* i
(3).struts2中的OGNLContext是实现者为ActionContext,它的结构为:OGNL Context:ValueStack(值栈,他是根对象),parameters,request,session,application,attr,当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action,然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问
$ l! d6 L( Q) k& |$ U
* V" a1 B1 D6 K5 y. |: Z& I ] a(4).当要访问某个对象只需在其前面加上一个'#',例如:#request,当然有一个特殊的例子,就是根对象,会省略'#',OGNL会设定一个根对象(root对象),在struts2中根对象就是ValueStack(值栈),如果要访问根对象(即ValueStack)中的对象的属性,则可以省略#命名空间,直接访问该对象的属性即可.: [9 J% W' G$ }6 q2 ~# o J
8 @" c) I# t) g1 K8 {2 ] w$ r(5).在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的root变量,就是使用它存在一组对象,在root变量中处于第一位的对象叫栈定对象(存放action),通常我们在OGNL表达式里直接写上属性的名称即可访问root变量对象的属性,搜索顺序是从栈定对象开始寻找,如果栈定对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。大家注意:struts2中,OGNL表达式需要配合struts标签才可以使用,如:<s:property value="name"/>,value属性接受的是OGNL表达式,搜索是否含有name属性
: ]" Q+ ~# }4 b; N% C
/ C7 X6 k4 p2 N# _6 \/ a& ^(6).由于ValueStack(值栈)是struts2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性:
, w/ q8 f0 O- l6 l/ g${foo}获得值栈中某个对象的foo属性0 u0 Z5 ^2 ?5 i: M
如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀
* P3 Y/ s/ j9 ^. F! v# N: s第一:application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username");
! y4 K( U7 C6 B第二:session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName");! ?" d5 s+ r* V8 h2 [. Y9 M
第三:request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribut("userName");
# Y& Y+ P% r) a第四:parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username");" O( f0 z% b3 O4 x7 A
第五:attr对象:用于按page->request->session->application顺序访问其属性.- { E( W0 }, T, j* e5 H
1 h5 j7 h$ j& G) K" b6 }# R- U(7).为何使用EL表达式能够访问valueStack中对象的属性:原因是struts2对HttpServletRequest做了进一步的封装,简单代码如下9 j1 }% }* l7 G
public class StrutsRequestWrapper extends HttpServletRequestWrapper{5 } v& W4 p; Q* }
publicStrutsRequestWrapper(HttpServletRequest req){
( Y$ b2 g6 J I9 Tsuper(req);! j9 A9 T+ X% a" t3 v, N5 T1 A8 e
}2 x' J0 \/ J, D# C8 x9 V0 g
public Object getAttribut(String s){' @5 a5 F9 G* ?' @
ActionContext ctx = ActionContext.getContext();, O/ ~+ j! O9 G) B2 n
Object attribute = super.getAttribute(s)//先从request范围获取属性值) w" @6 r, E& @ w
if(ctx !=null){
7 @0 n. B6 h+ b$ Q' v" k" ] if(attribute==null){! f6 z& @9 f( Z# c
...., |5 S5 c: U3 e) \
ValueStack stack =ctx.getValueStack();; S& W: h/ u ^
attribute=stack.findValue(s);//寻找规则就是前面的寻找规则
. [- @6 U) D' w1 s$ D$ [$ [....
1 ^2 y% O; t" f8 F& S1 M}
# c& H* k8 Z8 F}
# |7 d0 j ^8 x& ?! w' `return attribute7 `; } b5 P. \
}
; A. g9 \) [9 V x, n}" X+ `7 r) x8 ^
EL表达式只能访问ValueStack对象中的内容
" d9 f; {" c) [$ e& U+ R. J ' U1 i6 A! @! n8 a1 a% J& J& P
(8).采用OGNL表达式创建List/Map集合对象,如果需要一个集合元素的时候(例如List对象或者Map对象),可以使用OGNL中同集合相关的表达式,使用如下代码直接生成一个List对象:) ?- ]. k. W4 ?8 C
<s:set name="list"value="{'zhangming','xiaoi','liming'}"/>把当前迭代的对象放在值栈的栈顶- Q0 b0 n+ d* R0 d% p- Y, e
<s:iterator value="#list">
* d6 Z! S! `$ l. p- u; ^/ _<s:property/><br>" }& _1 q. y5 h- g. x
</s:iterator>+ n$ m1 U0 W" P2 Z
Set标签用于将某个值放入指定范围
$ w: j( C* j: a" [+ Tscope:指定变量被放置的范围,该属性可以接受application,session,request,page或action.如果没有设置该属性,则默认放置在OGNL Context中.. n+ C4 G( N& D. Z( l% W
value:赋给变量的值,如果没有设置该属性,则将ValueStack栈顶的值赋给变量+ Z) Q/ o' u5 k
生成一个Map对象:6 G; M1 u+ x# e& m$ b1 S) G, x
<s:set name="foobar"value="#{'foo1':'bar1','foo2':'bar2'}"/>1 Z1 _3 H7 r: \9 `7 k5 P
<s:iteratorvalue="#foobar">//迭代标签,把当前迭代的对象放在值栈的栈顶(entry对象),foobar是Map对象和request等对象是同等地位,访问时需要使用'#'! |) ]+ _" C% f7 F n* \
<s:property value="key"/>=<s:propertyvalue="value"/><br>
/ s- [1 m6 ?1 _# H1 r( ^) ~</s:iterator>" R9 V; P+ g* f" _
数字不用任何符号,字符串使用单引号('),对于Map采用的是maps.entrySet()这个方式进行迭代的.* f! r! z0 R5 H/ G0 F) b7 T
: ~1 N0 p" K2 w& e3 k6 h(9).property标签用于输出指定值:
% e- A$ J) K# G+ j# t, u2 R<s:set name="name"value="kk"/>
7 w# D) Y+ _! t+ V/ v" @( R9 H<s:property value="#name"/>
" a/ X( Y- j W0 b. pdefault:可选属性,如果需要输出的属性值为null,则显示该属性指定的值
: D' e8 E5 a( p, l4 x$ T. \9 Descape:可选属性,指定是否格式化HTML代码* D, I5 N7 k4 A! Z: x+ [
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值
8 |! {: U2 J; \- e9 X6 X% Pid:可选属性,指定该元素的标识
! v& J7 t$ x% e0 @% w$ A ) i6 O4 L6 D* X) ?; O1 Z$ J) Z
(1)0.对于集合类型,OGNL表达式可以使用in和not in两个元素符号,其中in表达式用来判断某个元素是否在指定的集合对象中,not in判断某个元素是否不在指定集合对象中,如下所示:; e- n7 P: K, B, W/ J
in表达式:
5 X7 B5 L. r' P4 A8 a<s:if test="'foo' in {'foo','bar'}">; m7 g2 H) Q" Q. ]7 y& n6 |
在
6 X0 ]+ Q; M# T; S j9 {</s:if>3 E7 P# }& V% I* z: F% A7 A5 V
<s:else>
" C# x) I5 |: r" z2 w# x3 o不在
. a4 b% k! g+ Z8 S</s:else>( R% v2 Q6 A/ ~* q# o
no in 表达式! ?" T. K+ J# Q+ D
<s:if test="'foo' not in {'foo','bar'}">4 f% I! I0 q; o c8 Y: ~/ o! {) F6 ~6 V
不在; a0 i' n9 F0 X1 k4 m7 b1 O
</s:if>" p+ n5 O$ b" P
<s:else>
1 @2 ?, l4 G! f在
0 k8 u* F% ^4 o$ C</s:else>
0 Q% [6 d0 Q: W2 O* N1 U8 H ?
+ d& k! |3 U0 @(11).OGNL表达式的投影功能,除了in和not in之外,OGNL还允许使用某个规则获得集合对象的子集,常用的有以下3个相关操作符:, l/ J: t5 M5 L" r+ t* z7 S
第一:?:获得所有符号逻辑的元素3 [6 I* a: _8 S/ L
第二:^:获得符合逻辑的第一个元素. Y6 c" Z) y5 O4 s4 `
第三:$:获得符合逻辑的最后一个元素" G4 F- E( a3 n8 f# E5 o3 m9 o" L. z/ s- p
例如代码:
1 G7 f2 y8 l( x<s:iterator value="books.{?#this.price>35}">4 {! u+ l; }& H( l* i& y. {' G
<s:propertyvalue="title"/>-$<s:propertyvalue="price"/><br>
6 k; T, v9 `9 S$ E</s:iterator>: h, @2 f" I% `- i6 T G9 m
在上面代码中,直接在集合后紧跟{}运算符表明用于取出该集合的子集,{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合,需要对大集合books进行迭代,this代表当前迭代的元素,本例的表达式用于获取集合中价格大于35的书集合.6 ^1 I+ [( t" h: ^
public class BookAction extends ActionSupport{
$ e! U2 ^6 K( s' d! Q! N8 I0 x: @private List<Book> books;
6 R( X* O; r4 c0 m
- I& y5 v# y' c* Y- `& u9 g8 v% |9 f@Override
' N$ j" m/ Q$ b! N9 N Epublic String execute(){3 f" `- }% j. i/ c
books = new LinkedList<Book>();4 I# E. d" w0 ~6 B
books.add(new Book("aadfsd","spring",23));7 }1 U% G" [/ ~4 @
books.add(newBook("basdfd","ejb3.0",15));
1 d9 W1 y& D" D9 w- K# b. V}! |) \% n0 E1 }% G8 o6 D
}
, f8 ~3 t0 m1 I+ `& e1 D' K
3 [5 f. T0 W1 A% w6 v8 \4.result配置的各种视图转发类型$ | e) T$ m7 i5 H8 x
(1).在struts1中有两种视图转发类型:容器内转发,容器外转发(重定向);
% W( P- p4 o/ w/ j% ]' N<actionpath="/control/employee/manage".../>) T2 v6 C8 a7 y$ p8 z
<forwardname="add">/index.jsp</forward>
9 c7 e7 Y" I6 n' h4 Q<forward name="add"redirect=""/index.jsp</forward>4 Y, s$ B, W% D& u8 n
</action>. ^: I" Y) e0 u) b3 T( t
; ~& |- M' \. f$ U' z- o) ~(2).struts2中的视图转发类型:result配置类似于struts1中的froward,但是struts2中提供了多种结果类型,常用的类型有:dispatcher(默认值)、rdierect、redirectAction、plainText;dispatcher对应于struts1中的容器内部请求转发,redirect对应于struts1中的容器外部转发(重定向); g9 Q) ^ I! C; p
( g" M1 ~- X+ I
(3).<actionname="helloworld"class="cn.itcast.action.HelloWorldAction">6 ]) T& \; M% s, K
<resultname="success">/WEB-INF/page/hello.jsp</result>! s2 N1 G% C+ v4 k% x- U; D. y
</action>
8 s8 b, J4 P9 w3 a8 S* a在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应的action中的属性,如下:<resulttype="redirect">/View.jsp?id=${id}</result>,使用重定向可能需要将Action中的数据属性代入视图页面,这种方式太重要了,很实用.这种表达式叫做ognl表达式,struts1中是没有的,只能将属性值在代码中写死了,不像struts2中的这个表达式,很灵活.当传递的属性是中文时,需要进行URLEncoder.encode("传智播客","UTF-8")编码.; c4 K0 X/ i& t5 U7 h u
+ K0 Z/ k- K8 {" T(4).下面是redirectAction结果类型的例子,如果重定向的action中同一个包下:
& W9 e. x+ k4 v' A8 x0 r<resulttype="redircetAction">helloworld</result>
0 ?- K2 ?; c6 G3 e9 L8 q9 _如果重定向的action在别的命名空间下:
( b% D, l: R# @, m/ a$ u6 o<resulttype="redirectAction">1 ~$ N( H: u8 Y/ U# _4 N9 T% |: Y
<paramname="actionName">helloworld</param>7 {3 k* y/ C$ y' ^
<paramname="namespace">/test</param>6 }: W+ S* j% T+ }/ c+ u
</result>& V. Q6 W( v2 A5 |, { l
当添加用户完后,可以回到一个用户列表,此时可以重定向到action
% V, Y* k5 u" @" t 0 |' @. k' k9 _% Y) H0 m9 X; \
(5).plaintext显示原始文件内容,例如:当我们需要原样显示JSP文件源代码的时候,我们可以使用此类型4 L- w x, h. c* Q& n
<result name="source"type="plainText">- M; D* x- q! [% Z2 _- C3 u- W
<paramname="location">/xxx.jsp</param>
$ J$ E3 |* Z- V: x& K6 ^. R( t<paramname="charSet">UTF-8</param><!--指定读取文件的编码-->. y) F! ]5 t+ P" f3 [
</result>
* l+ X& U' |; N0 l. Z J, j在Eclipse中jsp是用UTF-8编码存放的,当读取jsp的内容时,是用本地字符编码的,可能出现乱码,所以要设置读取文件的编码集.
( B; t6 W( f$ I* y: ~4 V6 S7 S5 i + \" G& z- e! O# l- b3 K
(6).浏览器重定向的JSP不能放在web-inf目录中,而请求转发的JSP可以放在web-inf目录中: w# B" @$ w9 x7 \9 E, t1 j
' G' O, h0 ^, ?; C% H7 k3 d(7).struts2中的全局视图:
* G& Z4 q5 t$ w% h0 M! H<global-results>1 L! S: m8 Z; a9 A- U) @( Z
<resultname="index.jsp"></result>& K* V; [' s0 @% Y ~' J" y: C
<global-results>
, F3 p) [% V8 Z; m和struts1中的全局视图是很相似的) S' ~8 |- [2 n; {. {
我们可以定义一个包,然后将全局视图的配置放到这个包中
) P) J% q( `/ Z. X0 J ! R+ e# H2 W, U! @9 D
5.struts2常用标签6 |, g0 @4 W$ A1 @5 W: U0 r* J
(1).property标签用于输出指定值:2 ^& Z/ U6 _6 b% J7 P! a; V0 L
<s:setname="name" value="kk"/>
+ D" t& b5 u8 L+ d# R4 O<s:propertyvalue ="#name"/>
9 S6 a/ m: v3 C, odefault:可选属性,如果需要输出的属性值为null,则显示该属性指定的值. V- I% m* r" `0 M4 L ?
escape:可选属性,指定是否格式化HTML代码, W$ K* ]# _; `* L
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值% ~8 m7 e8 a1 ]8 F+ f4 h
id:可选属性,指定该元素的标识% g, G* g% X7 K) T
+ N0 e. t# |3 }4 U(2).iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组
" S6 q( F7 ^' R+ i3 ]<s:set name="list"value="{'aa','bb','cc'}"/>
% U: d/ s: j. f1 {<s:iterator value="#list"status="st">
) m5 u0 x3 [) D$ ^<font color=<s:iftest="#st.odd">red</s:if><s:else>blue</s:else>>
& @( \ G$ `0 J# [<s:property/></font><br>' Z( Q4 { {: [7 S1 D; L) \4 K
</s:iterator>
7 `% a6 X/ Y3 t: Rvalue:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合,
" h0 o8 `" V7 G/ y. kid:可选属性,指定集合里元素的id(已被标注为过时) Z' @- R- c; i l% Z
status:可选属性,该属性指定迭代时的iteratorStatus实例,该实例包含如下几个方法:# `2 f8 o+ i& n5 p% u
int getCount(),返回当前迭代了几个元素
) L& R# q: t9 ?+ ^1 {int getIndex(),返回当前迭代元素的索引
0 ?$ b5 l, s, g1 ]" jboolean isEven(),返回当前被迭代元素的索引是否为偶数
( ~3 q2 b$ P- q, M d9 L! Nboolean isOdd(),返回当前被迭代元素的索引是否是奇数$ Z: ?$ [9 y* D4 J! T3 \, d5 G
boolean isFirst(),返回当前被迭代元素是否是第一个元素
5 ~5 q: T/ p. w1 ]8 M- Yboolean isLast(),返回当前被迭代元素是否是最后一个元素3 c0 K: Z5 I1 Q" [8 r( s) N) `
/ t. h, f, t/ m. L; Z(3).<s:setname="age" value="21" scope="request"/>
$ T( ?" C( v1 O+ t9 _, p<s:iftest="#request.age==23"> c( s1 _ A, e: O% I
23, F% u5 q7 o9 C
</s:if>
; x1 Q# f# l" ~& D9 K<s:elseif test ="#age==21">
9 l( G5 j! S( ? |: o21
; ^& V- B9 J. {5 b</s:elseif>. B9 `8 f' {6 }6 P( z) A
<s:else>2 Z) I% N( {+ S) ~) b4 `/ b
都不等
# j: s8 A4 b" {- E( C</s:else>
/ k# v- k, v" x% c$ q! _: S9 |8 \ 4 q# u" | C" r
(4).url标签:
. ~# g3 h7 r+ m2 J# M<s:url action="helloworld_add"namespace="/test">
5 C3 j1 E+ m6 u% p' e( a1 Y<s:param name="personid"value="3"/>+ R+ Y( E3 K! c* o
</s:url>
1 E* S3 E8 a( h1 g. F5 f生成类似如下路径:2 f9 B: @# m2 y, U( \
/struts/test/helloworld_add.action?persionid=3+ I1 m8 M# _0 L
. P8 s3 H/ o3 o( D. ^当标签的属性值作为字符串类型处理时,"%"符号的用途是计算OGNL表达式的值7 P4 H( s8 J2 C
<s:set name="myurl"value='"http://www.foshanshop.net"'/>
' q' B( C4 a8 S6 p0 d" u7 k+ o<s:url value="#myurl"/> u# a2 s" j9 c i
<s:url value="%{#myurl}"/>6 e) O. U, S- \7 |7 o1 @
输出结果:
( n- ]5 t; k* ?#myurl; F2 T' }( y4 r" {) j$ n
http://www.foshanshop.net
. C- n1 s7 d/ C: U4 l" v2 h
. v: d8 Y7 ]; s- j6 k(5).表单标签checkboxlist复选框9 y* ^: Z2 ?0 l9 q
如果集合为list
+ a9 {( W% p7 h) q" s u* a/ ?% N<s:checkboxlist name="list"list={'Java','Net','RoR','PHP'}" value="{'Java','Net'}"/>7 f1 I* t& e* X. G, H+ v T$ z; {' O
<input type="checkbox"name="list" value="Java"checked="checked"/><label>Java</label>
, Y5 b. F1 ~ s9 i E# i' }<input type="checkbox"name="list" value="Net" checked="checked"/><label>Net</label>
( `! @: O, `1 A9 J) v* m<input type="checkbox"name="list" value="RoR" ><label>Java</label># v, C( V, r2 Q7 o
<input type="checkbox"name="list" value="Java"/><label>Java</label>4 E2 V, ~2 ?! ` F5 {
如果集合为MAP
& b: i, p/ M. b: b<s:checkboxlist name="map"list="#{1:'aa',2:'bb'}" listKey="key"listValue="value" value="{1}"/>
7 t5 r9 z k1 k+ C生成如下html代码:
0 [6 y5 F' {- b+ m5 d<input type="checkbox"name="map" value="1"checked="checked"/><label>aa</label>3 }/ B/ d" W- k1 {) }6 q. Q2 _9 |
<input type="checkbox"name="map" value="2" /><label>bb</label>
6 _% S' `/ _+ E; N当然集合里面存放的也可以是对象类型5 S4 W4 [; s1 p* d5 u7 e
: p P: P" t8 I* q5 b$ D+ A
(6).单选框
' Z1 u, t6 R, u+ @/ a8 }<s:radio name="beans"list="#request_person" listKey="personid"listValue="name"/>9 f5 R% n+ i+ _% [
/ i( P" p0 D" X- z: J6 T+ t, ^
6.struts2的处理流程与Action的管理方式
9 B, E8 @+ T+ ](1).用户请求->(查看web.xml文件)StrutsPrepareAndExecuteFilter->Interceptor(struts2内置的一些拦截器或用户自定义拦截器)->Action(用户编写的Action类,类似Struts1中的action,针对每一次请求,都创建一个Action)->Result(类似struts1中的forward)->Jsp/html(响应)) A; o3 k% Q+ a4 |/ n) ^
" `- M: e p+ Q: a0 p
(2).StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它负责拦截由<url-pattern>/"</url-pattern>指定的所有用户请求,当用户请求到达时,该fileter会过滤用户的请求,默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入到struts2框架处理,否则struts2框架将略过该请求的处理,当请求转入struts2框架处理时会先经过一系列的拦截器,然后再到Action,与struts1不同,struts2对用户的每一次请求都会创建一个Action,所以struts2中的action是线程安全的.. {! I& S. F# }/ J' x' e# b
2 j% ?( P- m$ d" _! Q* ]
7.XML配置方式实现对action的所有方法进行校验' h8 Y2 t K: ^5 V$ G3 D. x0 w# P$ x
(1).基于XML配置方式实现对action的所有方法进行输入校验:
5 s& m: K* z. S+ z7 t使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中,ActionClassName为action的简单类名,-validation为固定写法,如果Action类为cn.itcast.UserAction,那么该文件的取名应为:UserAction-validation.xml,下面是校验文件的模板:7 D) ?9 G S) ]4 e
<validators>
. V# m, ?+ F- e: S' t7 k<field name="username">
0 Y# v1 R+ }. [& f" S$ V' ]<field-validatortype="requiredstring">
$ T, J2 O$ m A }$ F5 r! d4 X<paramname="trim">true</param># W9 z& c Z% y5 I
<message>用户名不能为空</message>/ s( I; j3 U' N6 J- U5 Y
</field-validator>) O) z4 P- E3 _. D
</field>
5 N6 e" B5 `! n2 G2 v; R</validators>2 a* _0 P+ P0 ]$ B5 s8 [7 K* n7 V* @ h
<field>指定action中要校验的属性,<field-validator>指定校验器,上面指定的校验器requirestring是由系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到,<message>为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为资源文件中的key,在这个校验文件中,对action中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,然后判断用户名是否为空.4 W# v0 r! {" O8 D
8 E3 I4 \( v) e9 @8 ]% L7 G(2).struts2提供的校验器列表:
, [. k8 F, R$ }" l3 [# ~' vrequired(必填校验器,要求field的值不能为Null)
- F/ ]3 c- [% o m4 q/ Frequiredstring(必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串取钱后空格)
N/ |$ J8 q. G8 {' S- Bstringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)9 s7 T6 s1 }% A- O W4 x
regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)/ V C. R* X& Y* b! |8 u& B: ?! f6 [% H' _
int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
2 X/ j! Q% q3 h% ydouble(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)* c! n! W0 J2 c0 j, V
fieldexpression(字段OGNL表达式校验器,要求field满足一个OGNL表达式,expression参数指定OGNL表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)! {2 q6 B G9 W, t3 y$ W
email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
: m0 b. w, N1 b. k) d: S( cURL(网址校验器,要求如果field的值非空,则必须是合法的URL地址)/ d# `4 O8 x+ n" ]9 ^
date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)' M: B* C' Z3 p; ]0 `* E! a0 z
conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
+ v! P$ E2 C2 }5 {9 }# Zvisitor(用于校验action中的符合属性,它指定一个校验文件用于校验符合属性中的属性)) K X3 T" N1 T1 l: j5 y
expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中) z) p$ T, B& o# N& w5 N3 g; s7 ~
& v3 v/ x/ R% z9 ~+ R T(3).![CDATA[文本内容]]:文本内容不会被解析,只会原封不动的当做文本处理
& L' D2 u4 i3 I8 }- z ) O0 j) i$ ^2 g( _& d& s. [
(4).编写校验文件时,不能出现帮助信息:" A/ u2 N I; J6 E
在编写ActionClassName-validation.xml校验文件时,如果出现不了帮助信息,可以按照下面方式解决:6 f) n5 e3 B6 q5 X
windows->preferences->myeclipse->filesand editors->xml->xmlcatalog:点击add,在出现的窗口中的location中选"file system"然后再xwork-2.1.2戒烟目录的src\java目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的时候,不要急着关闭窗口,应把窗口中的Key Type改为URI,Key改为http://www.opensymphoney.com/xwork/xwork-validaor-1.0.3.dtd; C+ }) k- w* L7 x0 y5 x
/ _. x( O+ V. y$ F
8.XML配置方式实现对action的指定方法校验
) {; S) ^- `" h+ w3 b(1).基于XML配置方式对指定action方法实现输入校验:
& \- a2 W, w3 Q6 C( E当校验文件的取名为ActionClassName-validation.xml时,会对action中的所有处理方法实施输入校验,如果你只需要对action中的某个action方法实施校验,那么校验文件的取名应为:ActionClassName-ActionName-validation.xml,其中ActionName为struts.xml中的action的名称,例如:在实际应用中,常有以下配置:8 S) q9 p% h8 |8 Y5 |: R; v
<action name="user_*"class="cn.itcast.action.UserAction" method="{1}">3 k; \* A6 j& R! `4 D9 Y
<resultname="success">/WEB-INF/page/message.jsp</result>
: J; h% F; }' j \2 Y& \1 w8 i<resultname="input">/WEB-INF/page/addUser.jsp</result>
7 c8 I$ I& t/ u9 f& \</action>, k8 t5 [4 V) K( ]% z
UserAction中有以下两个处理方法:
d2 M) e# O: i5 Y$ G" apublic String add() throws Exception{
+ ^+ z! h" f& n' f& Q# b. B
E T9 ]' h4 | R5 a) c6 |}
( {8 \. ]* J+ S9 h4 M; ppublic String update() throws Exception{3 X4 [6 {# m$ I2 E
* i$ u1 D ` R2 p/ r}7 }, g$ R0 O/ w* m3 \5 o, K
要对add()方法实施验证,校验文件的取名为:UserAction-user_add-validation.xml9 D* B. e$ k' h! c, N! {4 Z
要对update()方法实施验证,校验文件的取名为:UserAction-user_update-validation.xml
1 Q+ k8 t) i8 d' f- J# L ! j. P0 @- D: y5 Q5 w& f$ c0 y
(2).基于XML校验的一些特点:
5 j5 V: f/ k) Y- _: A( |当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
+ X( P# Z$ D& m8 y, jActionClassName-validation.xml9 p9 C- b* ^+ L$ I, K# Q- g
ActionClassName-ActionName-validation.xml
4 d: g( h9 ^' ]; {/ S; a( @* P3 H系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验,如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的规则。
7 a% F( s$ L0 m) G当action继承了另一个action,父类action的校验文件会先被搜索到7 H l% F6 |6 ^8 F
假设UserAction继承BaseAction3 H8 N0 C6 q$ q' `* n+ x
<actionname="user" class="cn.itcast.action.UserAction"method="{1}">: Z: s4 R( M( y
</action>
& Y& d; D7 k2 \6 r! `% O访问上面的action,系统先搜索父类的校验文件:BaseAction-validation.xml,BaseAction-user-validation.xml,接着搜索子类的校验文件:UserAction-validation.xml,UserAction-user-validation.xml,应用于上面action的校验规则为这四个文件的总和
- l( R, q) ~, q/ e
' c1 Q6 |' d5 X o" Y! @2 U9.动态方法调用和使用通配符定义action
. x$ I$ V6 g( y(1).在struts1中实现方法的动态调用:
+ w. u1 L4 A4 V<actionpath="/control/employee/manage" type="....DispatchAction"parameter="method"/>' a8 G8 X" K$ h0 |8 p
</action>3 x4 l/ g) v. h0 Z
/control/employee/manage?method=addUI- ?4 X3 F& q7 C2 e
但是Action必须继承DispatchAction9 u; M( R; m& H5 q0 P& y2 g6 U
/ P$ X8 O7 ^' A( W" K2 R/ U+ K(2).在struts2中有两种方式:. C/ D6 l+ `5 F" k! _5 r; x9 J
第一种:(struts2.1版本后就不建议使用了)是动态方法调用:如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法,如下: l. [5 I9 ]! g- x% _
public class HelloWorldAction{
. K" ^2 x* ]2 }( ^1 h/ I8 N7 vprivate String message;
( I' i) F L" l....
! L" a0 W! d" N0 G9 spublic String execute()throws Exception{) a5 d1 h; f. n% _
this.message="我的第一个struts2应用";$ r& I& l) v8 J8 @, K! u
}! }7 d/ J& i4 W
public String other() throws Exception{) s5 q! X; `1 G2 \7 Y- C
this.message="第二个方法";
: w3 k9 s0 h% ~return "success";2 v+ r8 j0 f! ?9 l% U
}
, g& J- u" M- l5 N}
7 p; f6 _$ f" l+ `9 Y假设访问上面的action的URL路径为:/struts/test/helloworld.action,要访问action的other方法,我们就可以这样调用:/struts/test/helloworld!other.action,如果不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用:
. I6 y( m. Y5 x" x<constantname="struts.enable.DynamicMethodInvocation"value="false"/>0 u# [, K6 `" ~* T
第二种:使用通配符定义action(推荐使用的) ]# e z# F5 G3 {; R/ X* Z4 b& b- w
<package name="itcast"namespace="/test" extends="struts-default">0 l8 G! {2 i, ~9 b
<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}>9 Q. I7 i6 r* c* I: G$ {' o
<resultname="success">/WEB-INF/page/hello.jsp</result>6 s- S# n/ P% d4 \. P
</action>
# v# h; Z) r* E, c! x! m</package>+ Q* i; }6 n6 r+ i# @6 I- _
public class HelloWorldAction{+ s2 C$ y4 [! r" ` q
private String message;, w) m6 a) T0 Q8 F8 s* F
....
9 U! T9 ^5 N/ Q$ d+ s9 A6 npublic String execute()throws Exception{1 m' v/ [/ d" a+ P4 \" k# S& d, C6 N: i
this.message="我的第一个struts2应用";4 r2 K; j9 y( v) q' H, w! m
}
2 n) Z: Z% l' j$ \public String other() throws Exception{0 R6 t8 P6 ]) n; _+ `' g* g" l
this.message="第二个方法";$ ?$ F2 J- |0 V5 b
return "success";; f* j# C4 ^# B. U4 W v- T
}; t; h$ w7 ?2 g% w$ y: x
} |1 I. l6 u; f Q
要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action4 ?- T+ ^5 k6 @7 \. q7 \
name="helloworld_*"后可根据多个*,method={1},'1'表示匹配*的位置# E1 ~4 z. g0 I$ U6 L
name="helloworld_*_*",method={2}:要访问other()方法,可以通过这样的URL访问:/test/helloworld_xxx_other.action
7 Z' V6 b/ `. { K" \+ B z
- k* ]. }' } a10.对action指定的方法进行校验
, h& ?! x% I3 {+ U* U4 `手工编写代码实现对action指定方法输入校验:
# R. F/ P$ G+ i' @; |0 G! Y9 b/ \通过validateXxx()方法实现,validateXxx()只会校验action中方法名为Xxx的方法,其中Xxx的第一个字母要大写,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息,(为了使用addFieldError()方法,action可以继承ActionSupport(),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息.
6 B: r) m4 ^9 i( Z. e: WvalidateXxx()方法使用例子:
& }$ U: E& x; `8 t( L7 [' @public Stringadd() throws Exception{return "success";}* d8 ^( `- J+ x6 z
public voidvalidateAdd() {
8 S: q* J( b& C! u6 Aif(username==null&&"".equals(username.trim()))this.addFieldError("username","用户名不能为空");
2 E* X6 e( o$ @" k. t3 `- s: ?}
: I, Q, S0 ^; g# K% E, l' a1 U) l' c验证失败后,请求转发至input视图:<resultname="input">/WEB-INF/page/addUser.jsp</result>7 d6 w6 ]/ z6 g% _! D, ?) }1 j
在addUser.jsp页面中使用<s:fielderror/>显示失败信息
$ _1 C; t5 Z/ H6 ~" y- q$ p! e! l
' u# Z* m& g$ Y( [& K9 |11.对Action中所有方法进行输入校验
- F/ l' a2 { M" F. B) E5 p, Z# e$ G y; Y(1).在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验6 k" U$ v* ~8 T3 g9 K( h- R' w) q
+ Q. [) x7 i6 }8 o, ]0 L7 N(2).对于输入校验struts2提供了两种实现方法:一种是采用手工编写代码实现,另一种是基于XML配置方式实现
" p9 p7 y7 _9 K* B1 e7 O) K2 Q
# R) O" C7 h8 U(3).手工编写代码实现对action中所有方法输入校验:通过重写validate()方法实现,validate()方法会校验action中所有与execute方法签名相同的方法,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport(),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息./ F3 ^$ W% x# _/ }1 }% \
validate()使用例子:
) k/ A; `. u, g: j3 l! U' Lpublic voidvalidate(){( U* u8 a' q& u# a9 {& Z/ j
if(this.mobile==null||"".equals(this.mobile.trim())){this.addFieldError("username","手机号不能为空")}else{if(Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matchers()){this.addFieldError("mobile","手机号的格式不正确");}}/ C" u/ t6 B2 w* u
}. K |& ?- @3 D/ W, `2 E, u
验证失败后,请求转发至input视图:
8 K* ]/ ]+ m/ I' r" n+ {/ j<resultname="input">/WEB-INF/page/addUser.jsp</result>
* l6 Q! Y7 Q. N$ P! f* u2 q在addUser.jsp页面中使用<s:fielderror/>显示失败信息3 i# _) D% ^/ a/ H/ e, Q* T
" }2 F9 N, r1 e3 n# b1 g- D+ w12.多文件上传& p3 D; }1 v5 I L7 s
(1).多文件上传,就是在一个文件上传的基础上,将属性File变成数组类型File[]类型即可,同时该字段的名称必须要和上传页面中属性name的名称一样.
# r- g `+ c c) B: h w/ ]2 P. j然后进行一次迭代,就可以得到所有的文件/ w4 w$ h3 K; B9 y: u6 w6 x- [
l4 V# t3 |$ d6 X) c5 n13.防止表单重复提交5 r5 p; U- g) \/ d
<s:token>标签防止表单重复提交. p6 C* ]5 w0 o( T4 C8 ~) L! ^5 A
第一步:在表单中加入<s:token/>
% V6 f& x7 d) F( \<s:form action="helloworld_other"method="post" namespace="/test">9 e# @; Y) A! A
<s:textfieldname="person.name"/><s:token/><s:submit/>8 T; b7 `5 K5 _, P3 O
</s:form>
/ z2 S* S- |( ]5 {第二步:<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}">
" e1 y8 z B7 k<interceptor-refname="defaultStack"/>
. _( \! }$ q- U& _9 b<interceptor-refname="token"/>
$ U3 T7 n4 E# `' |& N) o; S<resultname="invalid.token">/WEB-INF/page/message.jsp</result>
+ j- X% G1 C0 v: d<result>/WEB-INF/page/result.jsp</result>
" L4 }: H/ y* L6 d$ {! E</action>
9 Q) l, z' W; Z0 j$ a以上配置加入了"token"拦截器和"invalid.token"结果,因为"token"拦截器在会话的tlent与请求的token不一致时,将会直接返回"invalid.token"结果# t+ z7 C" x( J2 |3 ~
在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误
. f T5 h/ p4 ^# c& |9 H使用了<s:form/>标签可以不指定action的上下文标签路径,可以通过命名空间实现.和前面的原理是一样的,在路径后面添加上sessionid号,只是这步操作不需要我们自己得到sessionid号,struts帮我们操作.3 N) j& m7 ?; Q1 p
在值栈中的对象,访问无需添加'#'
' X" p- I* z, |; o9 t
+ S* U; M7 e T) A) t9 Z/ a14.访问或添加几个属性# r) \, K: u& G8 J5 Q e' P
(1).访问或添加request/session/application属性,在struts2中的Action中的execute方法中没有Servlet api(没有响应的参数);) X& m7 q" d- @% S; {+ T4 k3 d
public String scope() throws Exception{
$ Y# t- V2 \# ~$ v: o3 ?( G4 aActionContextctx=ActionContext.getContext();
. Q% V/ R3 [( T" o$ zctx.getApplication().put("app","应用范围");
; ?) b; @: } Y9 ]+ F$ fctx.getSession().put("ses","session范围");9 Y. x. y% j) k' E, D
ctx.put("request","request范围");
: ]# o2 u4 X1 b( U- nreturn "scope";( @ g2 u4 K, j3 v8 l6 u6 q; d) z
}
: O8 A' R. [% K! g* H x3 x4 a 0 U; I/ A4 |$ K2 ?% o
<body>4 f( z, l/ Y1 |' V
${applicationScope.app}
0 o( m0 g0 v/ T s${sessionScope.ses}+ g: z: e5 n. t! k6 j/ A* D. B
${requestScope.req}( K" k0 h) U7 g
</body>0 u8 X7 }) M! W5 \) |% o! S
) d3 ~$ z2 g( @; q8 Q(2).获取HttpServletRequest/HttpSession/ServletContext/HttpServletResponse对象:( r; ?# A/ W, `. k8 \# u ~
方法一:通过ServletActionContext类直接获取
8 Z7 S2 Z" H" |9 h6 z8 j7 cpublic String rsa() throws Exception{$ r: J0 P& I* l- \; l
HttpServletRequest request =ServletActionContext.getRequest();4 U1 O) m5 ^' e- E) W
ServletContextservletContext=ServletContext.getServletContext();/ k9 \8 [9 A! \$ s6 F( D4 |/ z% W
request.getSession();" c7 N+ E0 ~5 w- q& G9 t4 V& L
HttpServletResponseresponse=ServletActionContext.getResponse();
: S/ u s5 d6 s- freturn "scope";* ]/ V1 T3 w" X5 g
}5 v8 g# @$ l$ S4 C
方法二:实现指定接口,由struts框架运行时注入:: R, ]0 B- P2 T. W0 J! f7 z
public class HelloWorldAction implementsServletRequestAware,ServletResponseAware,ServletContextAware{& A' _: Q* X) O9 ?! H4 @
private HttpServletRequest request;
( p" s: m4 U! c) f, fprivate ServletContext servletContext; C8 x; h! S- a* S+ O% ]! k
private HttpServletResponse response;7 K' D/ c3 q3 v
public voidsetServletRequest(HttpServletRequest req){+ r. d( F6 `3 t# M* z7 ^
this.request.req;
5 f6 R' Y* z8 P1 a1 Q}, L3 U1 p1 c, s9 B/ ~4 h) P
public voidsetServletResponse(HttpServletResponse res){: _* j, i4 J" f* N* G3 `
this.response=res;
: [# [3 R H0 a$ @$ p" w# p3 N}
$ X0 Q" N$ w# @# G: {public voidsetServletContext(ServletContext ser){
1 z; ?. _8 k5 Z2 r& a. Q% x' F' z. {this.servletContext=ser;6 t+ d) ?7 ~4 F1 W0 ~: n
}
6 b7 z( k4 V. D: o2 u, H2 X" ^注意1和2的不同,一个不需要得到对象,一个需要得到对象,所以要区分两个的应用场景! y# M! p+ G: L& b. X- k0 g
}
7 f+ o0 L, b( X5 V+ v F5 t
7 P6 R* V" E! U8 k3 B. {15.解决struts配置文件无提示问题
1 O2 O' z* v k9 \3 A& S& }0 ^找到struts2.0.dtd文件即可,windows->preferences->MyEclipse->XML->XMLCatalog,点击添加strut2.dtd8 Z$ J& U% u9 Q$ j
( l9 C8 Z- ~& m2 F7 K% D, s6 y" p16.介绍struts2及struts2开发环境的搭建
. p/ |' Q: d) L! j(1).struts2是在webwork2基础发展而来的,和struts一样,struts2也属于MVC框架,不过有一点大家需要注意的是:尽管struts2和struts1在名字上的差别不是很大,但是struts2和struts1在代码编写分割上几乎是不一样的,那么既然有了struts1,为何还要推出struts2,主要是因为有一下有点:' ~& j9 g( _& ?- u1 C; V: Y+ G6 w
第一:在软件设计上struts2没有像struts1那样跟Servlet api和struts api有着紧密的耦合,struts2的应用可以不依赖于servlet api 和struts api,struts2的这种设计属于无侵入式的设计,而struts1却属于侵入式设计,因为其的' w8 r0 z, W p. ]7 \. C& E
execute()方法中的参数为ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse
* D! v" O+ Z) y7 n1 i第二:struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能' ?* A$ g4 C; D& K) j# z' Q
第三:struts2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型,在struts1中,如果我们要实现同样的功能,就必须向struts1的底层实现BeanUtils注册类型转换器才行, N2 M* L7 L' T' n5 t
第四:struts2提供支持多种表现层技术,如:JSP,freeMarker,Velocity等, S% x# e: p: m: L# B/ Q- I, d
第五:struts2的输入校验可以对指定方法进行校验,解决了struts1长久之痛,struts1中的validate方法对所有的方法进行校验" d, b* h, w h8 z o
第六:提供了全局范围、包范围、和Action范围的国际化资源文件管理实现.
! D/ [' f) B$ [) C* v1 h
" \3 \( |. X( Y& k. n% E(2).搭建struts2的环境和struts1是相同的,第一步导入相关包,第二步建立struts2的配置文件,第三步在web.xml中注册struts2框架的配置: X6 G$ C- H1 {! G' g
) |* p. y* ?: a
(3).所需的包:struts2-core-2.x.x.jar,xwork-2.x.x.jar(webwork的核心架包),ognl-2.6.x.jar
' ^& N1 R$ h8 f. F5 t) s7 ]; K
. K8 P r1 y+ W( F1 G7 N7 \+ z' L(4).struts2默认的配置文件为struts.xml,该文件需要放在/web-inf/classes目录下
/ s% y0 y# Z+ u1 T
2 |/ n9 k9 B/ Y2 k(5).在struts1中,struts框架是通过servlet启动的,在struts2中,struts框架是通过Filter启动的,它在web.xml中的配置如下所示:可以参照struts文件夹下的例子中拷贝,在strutsperpareExecuteFilter的init()方法中将会读取类路径下默认的配置文件struts.xml完成初始化操作,注意:struts2读取到struts.xml的内容后,以javabean形式存放在内存中,以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件; S! i" T9 X8 G" @. I
2 k0 @8 X8 W4 d. u, u+ ], ]; M(6).自从struts2.1.3以后,下面的FilterDispatcher已经标注为过时了,struts2.1.3后期版本为StrutsPrepareAndExecuteFilter类1 v: c$ q0 j ^0 a6 [4 J
7 b9 Y& n* `% T, ~3 m/ L+ a
17.开发第一个应用
. P4 N. p+ }) }* k(1).在struts.xml中的配置: B- d3 h- j- B! v" H
<package name="itcast"namespace="/test" extends="struts-default">
6 ?. N$ p1 t8 m! ]1 g4 H <actionname="helloworld" class="cn.itcast.action.HelloWorldAction"method="execute">
$ Z: y) b5 i. `; e <resultname="success">/WEB-INF/page/hello.jsp</result>+ a; E2 Z( j$ Z D ?
</action>
% F( C0 p+ x* U, A) N# C, i</package>4 t0 f2 N; [' I9 |
在struts2框架中使用包来管理Action,包的作用和Java中的类包是非常类似的,它主要用于管理一组业务功能相关的action,在实际应用中,我们应该吧一组业务功能相关的Action放在同一个包下% i/ w' c" n n( e
配置包时必须指定name属性,该name属性可以任意取名,但必须唯一,它不对应java的类包,如果其他包要继承该包,必须通过该属性进行引用,包的namespace属性用于定义该报的命名空间,命名空间作为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径为:/test/helloworld.action,namespace属性可以不配置,对本例而言,如果不指定该属性,默认的命名空间为" "(空字符串).当然配置可以减少重复的代码,struts1中的重复代码就可以使用命名空间来解决
" `% ]; X( x: g; n# _通常每个包都应该继承struts-default包,因为struts2很多核心的功能都是拦截器来实现的,如:从请求中把请求参数封装转到action、文件上传和数据验证等都是通过拦截器实现的,struts-defaul定义了这些拦截器和Result类型,可以这么说:当包继承了struts-default才能使用struts2提供的核心功能,struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定义,struts-default.xml也是struts2默认配置文件,struts2每次都会自动加载struts-default.xml文件,包还可以通过abstract="true"定义为抽象包,抽象包中不能包含action,可以查看struts-default.xml文件中,就可以看到定义了很多拦截器5 S; |, L& \0 q; m
<result></result>和struts1中的forward很相似,定义视图
# J" B3 @: I( l7 X0 j + f9 u9 e/ g0 _5 ^
(2).public Stringexecute(){return 视图的名称;}注意到这个方法和struts1不同,没有参数,返回类型也不同,这就降低了耦合性,非侵入式的编程了.
: u3 b! l4 L9 t2 [ - D, I( A' K7 L. a
(3).在jsp中使用el表达式即可${message},message是Action中的一个方法getMessage()方法,而不是根据Action中的成员变量message
& ]+ e1 A0 E/ ?5 [* c & ]/ z( m8 A: S4 U" P3 M
18.配置Action范围国际化资源文件+ X% m3 c( N: q' b
(1).我们也可以为某个action单独制定资源文件,方法如下:在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为Action类的简单名称当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找,如果没有找到对应的key,然后沿着当前包往上查找基本名为package的资源文件,一直找到最顶层包,乳沟还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中查找5 k. S! }' P" h [3 s8 W
$ f7 i* v$ w- E) B4 s7 _
(2).JSP中直接访问某个资源文件. f% M- U5 h" C2 V' x3 ?
struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:
" H3 R5 B) i/ E% i<s:i18n name="itcast">1 W1 \2 X1 z$ r
<s:text name="welcome"/>7 \) B% J7 h6 m
</s:i18n>' t1 X U$ S8 O; J# @+ v; p) X: i1 |# x
itcast为类路径下资源文件的基本名
' T, m& H) E G8 \" O s1 r/ y如果要访问的资源文件在类路径的某个包下,可以这样访问:- L4 {1 v3 D7 O3 H: M
<s:i18nname="cn/itcast/action/package">
; G U( H" W e% T) b- c<s:text name="welcome">: b1 e3 \; Y% _
<s:param>小张</s:param>. ?% r9 h# o+ F' b9 {' l% ?
</s:text>" h" R+ i5 w4 _
</s:i18n>
5 f3 E, `; f8 ?$ M( z+ V2 m, L上面访问cn.itcast.action包下基本名为package的资源文件( d3 C+ P. S/ K. t& N* R) P- x
! @; I8 O$ W1 Y) q2 B) _) m2 f
19.配置包范围的国际化资源文件
; c+ u7 k2 C$ b(1).在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化的内容都放置在全局资源属性文件中,显然会导致资源文件变得过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件
2 [9 ^+ P0 q" `方法如下:在java的包下放置package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源,当查找指定key的消息时,系统会先从package资源文件中查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找.
5 _" ?$ z& k3 U& | 2 G2 V4 G/ w9 z5 \1 Y- U' ?
20.配置国际化全局资源文件、输出国际化信息( g6 Z, o( l4 L8 B' m$ }
(1).准备资源文件,资源文件的命名格式如下:- K2 }1 g) [ a" Y" D' L8 ]
baseName_language_country.properties* J0 j4 ~! t) `2 m. Q4 N+ r
baseName_language.properties: `+ x: g; K) b1 ^9 R5 ~' V
baseName.properties6 m/ a! F+ J* i9 g
其中baseName是资源文件的基本名,我们可以自定义,但是language和country必须是java支持的语言和国家。如:
! s S5 c- [3 {% [中国大陆:baseName_zh_CN.properties' |2 p6 _% o% d/ A2 s( \7 H0 h* j
美国:baseName_en_US.properties5 y4 ~6 u) I# u
2 y7 o ^. }( v( f# \8 k* ]8 y(2).现在为应用添加两个资源文件: ]4 X, l$ {4 ]( w' ^
第一个存放中文:itcast_zh_CN.properties
8 U+ W( {3 p$ d9 Q- U内容为:welcom=欢迎来到传智播客/ d9 m$ Q8 P$ @# @8 s$ H
第二个存放英语(美国):itcast_en_US.properties+ x1 Y" d$ W3 L% N" Q( G0 D' y
内容为:welcome=welcom to itcast5 ]: U# B8 u: F1 ~/ s
+ l, z5 a! b4 g) A, l(3).对于中文的属性文件,我们编写好后,应该使用JDK提供的native2ascii命令把文件转换为unicode编码的文件,命令的使用方式如下:
# U+ ~5 t1 X# X x m* S: mnative2ascii 源文件.properties 目标文件.properties,在MyEclipse6.6版本以及后面的版本会自动转换.4 c6 i5 _% B/ y+ J( [+ T
. V( J( G) K& |
(4).struts2有:全局范围,包范围,action范围的资源文件
8 Q& | _" j W. i2 R0 w: |, ? n # R! W. ^9 `* h; S$ r
(5).配置全局资源与输出国际化信息:
) k0 R# T- q+ \- @' K当准备号资源文件之后,我们可以在struts.xml中通过:struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:. y) Y, v2 C: c( r1 y
<constantname="struts.custom.i18n.resources" vlaue="itcast"/>% V- p! R/ K3 l
itcast为资源文件的基本名
2 Q: L) \0 v! _: r5 v; b* W- H后面我们就可以在页面或在action中访问国际化信息:2 R% p' j0 ^+ c
在JSP页面中使用<s:text name=""/>标签输出国际化信息:
# X0 @- i& L5 G& t( h1 I1 n- z- H<s:textname="user"/>,name为资源文件中的key
- l+ |$ p9 y1 J: a! b2 d; K在Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该该方法的第一个参数用于指定资源文件中的key,
( _( Y* w6 A8 l$ R" i/ p% m# l在表单标签中,通过key属性指定资源文件中的key,如:- B5 Q6 A. {: @+ Q$ B: [
<s:textfieldname="realname" key="user"/>
: x1 L+ ~6 i4 ^! t
* G# q. _3 W5 i! @! f21.请求参数接受+ I! w3 x0 ~5 M
(1).struts1中是使用ActionForm接受用户的请求参数2 w( b# G- {3 L) S/ e
' n6 c, p4 i* o8 e1 v(2).采用基本类型接受请求参数(get/post):& c$ ]9 f9 R2 a' x* e1 z% `' B
在Action类中定义与请求参数同名的属性,struts2便能自动接受请求参数并赋予给同名属性:请求路径:http://localhost:8080/test/view.action?id=78. N: c5 {- n5 Z* ^: I, D/ w& f2 u. O }
public classProductAction{
k1 h$ Z2 a- o9 r; b/ Y* C0 Qprivate Integerid;! ^3 J, e' Q8 u
public voidsetId(Integer id){//struts2通过反射技术调用与请求参数同名的属性的setter方法获取请求参数值
5 E' B4 i* E- l. z% ithis.id=id;# C) K- s% y, m6 M4 L. }2 s. C
}
5 I. D: d( s- e7 u* npublic IntegergetId(){return id;}
0 |! {+ ^+ k7 Y! f& d2 v6 Y}
; p2 ]( ^0 \7 V( |: F: y- h 1 G+ w W8 G6 V: |
(3).采用复合类型接受请求参数
V7 _! V8 Z9 o( U0 N" V9 `请求路径:http://localhost:8080/test/view.action?product_id=78, |! c2 M5 ]; U+ w4 p9 @6 y1 G
public class ProductAction{4 B% W" }5 S: ^. q8 Z
private Product product;
0 G3 d+ ?; x0 epublic void setProduct(Product product){htis.product=product;}
( {3 |9 Z7 k7 \8 l( B! D/ `public Product getProduct(){returnproduct;}
1 F8 X" n i& C}% e/ X6 T9 h! z& r
struts2首先通过反射技术调用Product的默认构造器创建product对象,然后再通过反射技术调用product中与请求参数同名的属性的setter方法来获取请求参数值
; H! u7 T5 H. ^0 O
7 K& v; f# n+ D9 s7 p(4).关于struts2.1.6版本中存在一个Bug,及接受到的中文请求参数为乱码(以post方式提交),原因是struts2.1.6在获取并使用了请求参数后才调用HttpServletRequest的setCharacterEncoding()方法进行编码设置,导致应用使用的就是乱码请求参数,这个Bug在struts2.1.8中已经解决,如果你使用的是struts2.1.6,要解决这个问题,你可以这样做:新建一个Filter,把这个Filter放置在Struts2的Filter之前,然后再doFilter()方法中添加以下代码:
2 h z, J, O% w% u' F1 K# hpublic void doFilter(..){3 } S+ u8 m" K) B( t/ t5 B9 Z
HttpServletRequest req=(HttpServletRequest)request;* u0 Y Y. j( r1 T
req.setCharacterEncoding("UTF-8");
/ ~7 O3 A) m! ^, t4 u1 A* s6 rfilterchain.doFilter(request,response);
# C" |, I9 J1 \, |4 o}7 ]/ {( I$ F" v' W- P7 O* G, v
9 b' w. p T& e2 M4 _
22.全局类型转换器
: C$ \* q' k" w. K自定义全局类型转换器:将上面的类型转换器注册为全局类型转换器:在WEB-INF/classes下放置xword-conversion.properties文件,在properties文件中的内容为:待转换的类型=类型转换器的全类名
( W T$ f( {; r2 _对于本例而言,xwork-conversion.properties文件中的内容为:
: x9 ^, j$ V$ v U; y: t/ qjava.util.Date=cn.itcast.conversion.DateConverter1 o% a; x6 l8 Q" a
2 H! m' a: v8 S1 d
23.输出带有占位符的国际化信息
2 m7 j1 _" o3 w* h1 k/ N; d" ^% y(1).资源文件中的内容如下:
! G$ z/ L4 J; V+ Zwelcom={0}欢迎来到传智播客{1}% c6 L5 l& ]- E8 @/ j& W
在jsp页面中输出带占位符的国际化信息
9 @& w9 K5 o# k4 k% A<s:text name="welcom">) u: V4 u4 \5 S
<s:param><s:propertyvalue="realname"/></s:param>+ t | S; A; F- Q- ?
<s:param>学习</s:param>
0 q: d: ]* ^+ f4 k</s:text>, ~& A0 x# C: L% _3 H2 G) G
在Action类中获取带占位符的国际化信息,可以使用getText(String key,String[] args)或getText(StringaTextName,List args)方法.
! T3 h. ?# B# m' X0 n % S6 A; g+ b5 e
(2).占位符就当是一个变量参数,可以传递给定的参数值.
/ I7 Z! G" m8 Z4 P1 F4 q: c
- F$ I$ j# r. d0 n- W9 O$ z1 T24.为Action属性注入值. \! J" Q- C5 T8 C- p9 x
struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便的为Action中的属性注入值,注意:属性必须提供setter方法,* [7 S! M, Q3 D$ n
public class HelloWorldAction{
# i3 y( g9 ~3 D! B+ ^0 g0 r private String savePah;/ f6 _! y; z6 Q
public String getSavePath(){& p% h W6 c. `9 d
return savePath;
. F V/ R" G& K }
+ W0 c6 w1 l1 r& e& L' | s public void setSavePath(String savePath){
- |' b7 ?/ t; E6 ]2 M this.savePath=savePath;( {7 k. P9 |( s ]' g d* y
}
% B$ J; J( Z7 j7 l4 H; L& ^0 o. n}
6 E! X w8 e5 G$ \<package name="itcast"namespace="/test" extends="struts-default">4 `, N& V2 x3 D, C6 B; V5 [
<action name="helloworld"class="cn.itcast.action.helloWorldAction">
4 \7 O) T4 `1 a+ J' X ~3 B<paramname="savePath">/images</param>6 c3 F) p9 i0 V ]9 \
<resultname="success">/WEB-INF/page/hello.jsp</result># M) \" T/ K' B
</action>( w* {/ b) v2 ]3 j- w% N
</package>0 X" T% G. |1 w9 ?9 @
上面通过<param>节点为action的savePath属性注入"/images";Action的变量的值,不能写死,经常变换,需要通过配置来设置参数& {5 B% F1 I2 `% Q- O
' c+ g; U3 N+ F, Q% R5 I4 Z: C25.为应用指定多个配置文件& }: P, m+ ]# E3 X) R+ N z( _, z' R
(1).在大部分应用中,随着应用规模的增加,系统中的Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿,为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以讲一个struts.xml配置文件分解成多个配置文件,然后再struts.xml文件中包含其他配置文件,下面的struts.xml通过<include>元素指定多个配置文件:. R3 J' \ H" T: D# Z9 Z
<struts>
" F7 ]/ S- |; U2 `+ l9 A0 i<includefile="struts-user.xml"/>, J0 G# @2 |6 U/ A
<includefile="struts-order.xml"/>
. h, z: y3 z7 ?" t</struts>$ i! _9 E. v4 l) \
通过这种方式,我们就可以将struts2的Action按模块添加在多个配置文件中
/ N/ e8 I% C3 X9 `' r0 Q- ? % @% b- O' X: {! [- W2 b/ Z7 E1 y6 ^2 R3 C
26.文件上传/ [, \, Z' _3 c/ p4 O/ s% e% i& M8 h0 k
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar,这两个文件可以从http://commons.apache.org下载,在struts2.1以前的版本需要添加,以后的版本就不需要添加- y& b/ v/ h P0 R5 F* B
0 Z2 @7 K4 M$ Q- \3 \5 ~
第二步:把form表的enctype设置为:"multipart/form.data",如下:
& `: ]) w# I% P<form
% i9 _! T' e' M7 H; e8 [, R% m( jenctype="multipart/form-data"action="${pageContext.request.contextPath}/xxx.action"method="post">
7 E8 L# ~# R8 j0 R. m2 T2 v8 o<inputtype="file" name="uploadImage">这个属性的name必须要和类中File名称一样$ y% E+ V' {# T$ i. l
</form>
8 x8 r8 U% J6 @8 i4 ^' r! C6 E第三步:在Action类中添加以下属性5 ]" {: j% k( o1 J; A3 o, Z
public class HelloWorldAction{3 M! \7 h+ K; X
private File uploadImage;//得到上传文件;: r/ T/ b Y/ t T, i1 Z
private String uploadImageContentType;//得到文件的类型
5 n* j2 w6 K+ e9 H0 l- u# x3 Y+ Sprivate String uploadImageFileName;//得到文件的名称
8 s: K! z, Z4 t; m//这里省略了属性的get/set方法(但是要注意get/set方法是必须的)7 Q% _% B! H: q2 K" k
public String upload() throws Exception{! a+ e+ s- q9 c; B- o! _6 o
String realpath =ServletActionContext.getServletContext().getRealPath("/images");5 W! B+ i& [7 B, c2 w
File file=new File(realpath);
. a; i X) M% l. U* Y2 A8 f( mif(file.getParentFile().exists())file.getParentFile().mkdirs();//目录是否存在,不存在就创建
L3 O8 g( ?" @/ nFileUtils.copyFile(uploadImage,newFile(file,uploadImageFileName));
+ J& p% i R3 x# X! `9 H* A2 mreturn "success";
$ v" N5 I. R, U, H9 T}
/ u" G8 p4 ~$ O}8 m0 j8 {1 v3 o8 P! k
( w+ k7 Y$ p- E$ j
(1).如果文件不保存,struts2会把文件保存到自己的目录中,但是当这个Action执行完后,该文件就会被删除,所以我们要将上传的文件保存到硬盘上
' ^" J$ h- @( L" |9 W
3 H* m% N- y1 L+ S. M; H# J/ x" ](2).最好还要判断以下,文件uploadImage是否为空
! ?* W2 L* e# }3 Z% F6 i+ Y$ r
7 Z* _* [ M! S(3).如果上传大的文件,web都会失败,像一些门户网站上传视频,都是通过一个插件,可以把这个插件看成一个程序,只是这个程序是通过Socket变成的,针对一个端口进行传输数据4 L/ Y) |* ~, }( n4 ]
1 v9 `& g! w+ }1 R* `
27.指定struts2处理的请求后缀
) t+ j/ I. }- l% f(1).前面我们都是默认使用.action后缀访问Action,其实默认后缀是可以通过常量"struts.action.extension"进行修改的,例如:我们可以配置struts2只处理以.do为后缀的请求路径
+ A0 H% q: w- T$ f$ F7 x<struts>0 h7 s1 n- K% n8 N" Y+ @; b
<constantname="struts.action.extendsion" value="do"/>
8 P4 d* E& Q' H3 m6 u2 [' _</struts>
! l0 c, U+ |1 S如果用户需要制定多个请求后缀,则多个后缀之间以英文逗号","隔开,如:# P _# [9 g, c4 Z( R! J
<constantname="struts.action.extendsion" value="do,go"/>% Y6 d; S9 t( y4 ^; v
4 O" ?3 z3 ?) U) n$ D& ?8 m% i( S
(2).常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置,两种配置方式如下:
7 h; |* \/ f( E4 E在struts.xml文件中配置常量:$ t, w+ R, \9 ^9 {
<struts>) @1 _( _& T& R: Z' _: j
<constantname="struts.action.extendsion" value="do"/>) X; e2 K9 @! }) v) U& b' S
</struts>
) h& W1 i# k. e9 K A! s在struts.properties中配置常量:. A: ]/ ^4 b5 f% ^+ P& Q' n
struts.action.extension=do8 ^) L! s. p; o2 R7 W* u- M& `
因为常量可以在下面多个配置文件中进行定义,所以我们需要了解struts2加载常量的搜索顺序:
1 P4 L7 R5 R& s& w/ _' Fstruts-default.xml6 M, a- k6 a7 |/ u
struts-plugin.xml: R; f+ R7 t, R% w U0 c M
struts.xml( L0 h9 z; R9 ~$ p' O* @0 o
struts.properties9 g# i4 X7 r9 R8 R$ F
web.xml6 v* B# ]0 F5 S9 _, K, W% i
如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值
9 w' b0 Y. v! ]) K+ |
, I7 n' n2 w- \/ k* `! z(3)., a- x8 Y: [, X2 j
第一:默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的输出:% ~. P. i- P4 j
<constantname="struts.i18n.encoding" value="UTF-8"/>' @3 T- z& O: a* e- z2 X- E
第二:该属性指定需要struts2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由struts2处理,如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开: {5 G' V6 ?# u. Y6 ~
<constant name="strtus.action.extension" value=do"/>' a$ R+ y/ ^ D! s; l
第三:设置浏览器是否缓存静态内容默认值为true(生产环境下使用)开发阶段最好关闭,不然看不到修改后的数据
- b2 Y2 L( @- ^1 B. J* G<constantname="struts.serve.static.browserCache" value="false"/>1 v1 K+ }' F: h) D" m& r3 O1 z: O3 e
第四:当struts的配置文件修改后系统是否自动重新加载该文件默认值为false(生产环境下使用),开发阶段最好打开
. b& O, p8 M5 A9 n) P( I<constantname="struts.configuration.xml.reload" value="true"/>! ~! Y' i1 }% ~% \6 @* w
第五:开发模式下使用,这样可以打印出更详细的错误信息
& r+ h4 y* @: r8 D( t H<constant name="struts.devMode"value="true"/>
5 C- L) B3 U2 u, d; X% w第六:默认的视图主题5 R1 @4 H* V+ A# X3 Q* J, ^/ M
<constantname="struts.ui.theme" value="simple"/> k8 j& A% C, _0 @" ~, p# Z
第七:与spring集成时,指定由spring负责action对象的创建
5 T. ?# J" k" x, V3 E<constantname="struts.objectFactory" value="spring"/>
! I; ~% P+ V' R# v3 j第七:该属性设置struts2是否支持动态方法调用,该属性的默认值是true,如果需要关闭动态方法调用,则可设置该属性为false5 [1 R3 q1 u J2 @9 Y" S: y
<constantname="struts.enable.DynamicMethodInvocation"value="false"/>( i. L$ U4 S# l7 e( l, r" `/ O
第八:上传所有文件的总大小限制, N6 z) I( Q/ R4 W. ^
constantname="struts.mulitipart.maxSize" value="10701096"/>
5 @7 _! B p- g3 i: l4 F7 B) \
1 n: ~4 k! r1 s# h& j+ K& {8 ?28.自定义拦截器
0 ?( r. k/ n8 N3 W6 |(1).如果用户登录后可以访问action中的所有方法,如果用户没有登录不允许访问action中的方法,并且提示"你没有权限执行该操作"
1 d: M- q% j5 W2 K1 b8 ]/ h & ^1 D9 ~- F7 r# g$ T3 p0 Y
(2).
' {9 X, T) _: J. F<interceptors>
) U( o1 j9 e# L1 r/ N<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
; u/ f7 A. e) Z% [* D% f</interceptors>+ C7 X; X9 B- Z
<actionname="list_*" class="cn.itcast.action.HelloWorldAction"method="{1}">
+ t0 l4 _! P9 e3 i. b<interceptor-refname="permission"/>
- \) I1 C* k6 d7 v! U1 `! z2 I+ [如果为某一个Action定义一个拦截器,struts2中对Action的默认的很多拦截器都失去功能,所以要想做到两全其美,需要定义一个拦截器栈:
+ f+ T2 l% }- B2 [<interceptors>( r7 h6 }' P' B" f6 r
<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
$ s8 [7 V7 g- U9 f) E<interceptor-stackname="permissionStack">
9 r0 q- j0 ^0 \* `<interceptor-refname="defaultStack"/>$ R; v9 S' i9 ^
<interceptor-refname="permission"/>
8 U1 X9 m- \% ~+ N</interceptor-stack> z$ r, p4 A/ N
</interceptors>
. V1 K; T3 b, b. I* o S因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能,如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-refname="permissionStack"/>把拦截器定义为默认拦截器,注意:每个包只能指定一个默认拦截器,另外,一旦我们为该包中的某个action显示指定了某个拦截器,则默认拦截器不会起作用.
/ W% G0 o) \' K+ K0 Q. I* `) U(3).系统默认的拦截器可以到struts-default.xml中查看,很多功能.系统拦截器放在最前面,自定义的拦截器放在后面.6 [7 J u* `2 t% {" A
$ |, F' O3 i. @' N; M! u. Y' E
29.自定义类型转换器# U# q, a. k1 N6 u
(1).struts2中提供了两种类型转换器:局部类型转换器(只对某一个action起作用),全局类型转换器(所有的action起作用)
' i' U9 {/ [4 |& f2 ?, z; c( s 8 L7 a# }- T* f
(2).类型转换器必须继承DefaultTypeConverter最好用xwork2.jar中的,重写converValue(Map<String,Object>context,Objectvalue,Class toType){
* n9 y# V2 U8 {) r! |& mreturnsuper.convertValue(context,value,toType);" ~: R2 e9 y Y. F
}
; u# l: h/ Y- H+ K/ p( m$ h其中第一个参数和ognl表达式,第二个参数是需要转换类型的内容(是String数组,因为可能有多个值),第三个参数是需要转换成什么类型,要实现双向转换
4 o. C6 G* @( I9 l& w& Q8 \3 A 6 l# g0 v3 Q$ a+ p1 @" ]' P
(3).将上面的类型转换器注册为局部类型转换器:9 u9 `) r* K" k+ O
在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties.在properties文件中的内容为:/ F9 f* a( @5 m; x. ?4 X
需要转换的属性名称=类型转换器的全类名
s2 d: t1 Q; k1 }1 i) q, c对于本例而言,HelloWorldAction-conversion.properties文件中的内容为:) j1 b `& C/ m% r8 T( ?- ]
createtime=cn.itcast.conversion.DateConverter |
|