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