Struts2学习笔记(二)

主要说了些拦截器相关的内容,还有大量的 OGNL 相关的东西,最后补充了个注解,不过看到有人说 sturt2 几乎现在很少用了,大多数公司都用 springMVC ,还得抓紧时间继续学呐
还有一个模型驱动拦截器放在了笔记一中,因为这篇已经太长了….

注入对象

Action 既然只是普通的一个 java 类,如果想要获得 request 等内的数据应该怎么做的,这时候就用到反射技术来注入对象了;具体的注入实现是 ServletConfigInterceptor 这个拦截器,支持注入那些对象可以进这个的源码看看,就是一连串的 if
Action 中注入对象只需要实现特定的接口即可:
实现 RequestAware 接口注入 request 中的 map 集合,大多数都是用的这个吧
实现 ServletRequestAware 注入 request 对象
实现 ApplicationAware 注入 Application 对象
实现 ServletContextAware 注入 ServletContext 对象
另外还有 session 的,等等….

那么什么时候用注入对象的方式呢,ActionContext 和 ServletActionContext 都不能在构造器中初始化,因为拦截器是在实例化 Action 之后执行的,如果用的很频繁每个方法都来一遍太 low 了,于是可以使用这种注入的方式保存成全局变量
在后期优化的时候对 BestAction 进行注入也是不错的选择

Token拦截器

在表单防止重复提交中,这个是必用的吧;一般会用到两个域:一个是 page ,一个是 session,对应客户端和服务端吧
相比 Struts1 在 Struts2 中使用更加的简单:
首先在 JSP 页面添加 Token 标签,它会在被访问的时候创建令牌,是在 form 里面哦,另外最后也加个显示错误的标签,因为抛的错误是 Action 错误,所以…

1
2
3
4
5
6
7
8
9
10
11
<body>
<s:debug />
这是一个测试页面 <br>
<%--显示 Token 认证错误信息--%>
<s:actionerror/> <br>
<s:form namespace="/reg" action="TokenAction_reg" method="POST">
<s:token/>
<s:textfield name="name" label="用户名"/>
<s:submit value="提交"/>
</s:form>
</body>

其次配置 Action 的时候需要指定下栈排除一些不需要校验的方法,然后添加个校验失败跳转的页面

1
2
3
4
5
6
7
8
9
10
11
12
13
<action name="TokenAction_*" class="com.bfchengnuo.web.action.TokenAction" method="{1}">
<result name="success">/reg/success.jsp</result>
<result name="regView">/reg/tokenReg.jsp</result>
<!--指定 Token 验证失败后怎么办-->
<result name="invalid.token">/reg/tokenReg.jsp</result>

<!--指定拦截器栈,这样会覆盖默认栈,所以需要手动再添加一次-->
<interceptor-ref name="token">
<!--排除方法-->
<param name="excludeMethods">toReg</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
</action>

嗯….基本就是这样了,另外;如果想要替换错误信息为中文,可以在当前目录建立 Action 相关文件,修改 struts.messages.invalid.token 为你想要的值即可

看网上的帖子说还需要在 struts.xml 中修改 struts.custom.i18n.resources 常量;但是我测试不用修改也完全有效,版本好像是 2.3

execAndWait拦截器

常叫做为执行等待拦截器,从名字也可以看出了,就是在执行耗时操作的时候跳转到一个预设的界面,等耗时操作结束后再跳回;这个过程是个伪异步,因为在等待页面是通过定义 meta 的方式进行定时刷新检测的,下面来简单的模拟下,首先是 Action ,非常简单就是让线程休眠下…

1
2
3
4
5
6
7
8
9
10
11
12
public class WaitAction {

// 模拟耗时操作
public String execute() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
}
}

然后就是配套的配置文件了,execAndWait 是不在默认栈里的,以及需要配一个等待页面 wait:

1
2
3
4
5
6
7
<action name="WaitAction" class="com.bfchengnuo.web.action.WaitAction">
<result>/reg/success.jsp</result>
<result name="wait">/wait.jsp</result>
<!--执行等待拦截器不在默认栈中,一般添加到最后就行-->
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="execAndWait"/>
</action>

那个等待页面和成功页面就不写了,就是很简单的一句话而已,这样就能看出效果了

自定义拦截器

搞一个简单的拦截器测试看看,自定义的拦截器要实现 Interceptor 接口,注意导包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class LoginInterceptor implements Interceptor {
// 用来反序列化的,写不写无所谓
private static final long serialVersionUID = 5175604343895716587L;

@Override
public void destroy() {}

@Override
public void init() {}

@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
Object action = actionInvocation.getAction();
if (action instanceof LoginAction) {
// 直接放行
return actionInvocation.invoke();
}

Map<String, Object> sessionMap = actionInvocation.getInvocationContext().getSession();
if (sessionMap.get("username") == null) {
// 没有登陆,跳转到登陆
return "login";
} else {
return actionInvocation.invoke();
}
}
}

看以看出,逻辑非常简单,判断是否是 LoginAction 如果是就直接放行了,如果不是就判断 session 是否存有用户信息,没有就跳转到登陆界面上去,但是总感觉这样写不是很优雅呢……嘛~先这样吧
优雅一点的可以通过 ActionInvocation 对象获取 Action 的代理对象(ac.getProxy()),然后通过代理对象可以拿到当前执行 Action 的方法名等信息,然后进行相应的拦截
拦截器的 init 方法是在服务器启动的时候就会执行的(过滤器也是如此),并且一般只执行一次,而 intercept 方法会执行多次;如果你不需要初始化或清理代码,可以继承自扩展的 AbstractInterceptor 类,它提供了一个对 init() 和 destroy() 方法的默认的无操作实现。
接下来是测试 Action 了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class LoginAction extends ActionSupport implements SessionAware {
private static final long serialVersionUID = -9178854276859257157L;
private String name;
// 实现了 SessionAware 接口,以此来获得 session 中的 map
private Map<String,Object> sessionMap;

@Override
public void setSession(Map<String, Object> map) {
sessionMap = map;
}

// 简单模拟,只要用户名对就通过
public String reg() {
if ("admin".equalsIgnoreCase(name)) {
sessionMap.put("username", name);
}else {
return LOGIN;
}
return SUCCESS;
}

public String toView() {
return "toLoginView";
}


public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

与之相关的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<package name="Login" extends="struts-default" namespace="/login">
<!--配置自定义拦截器-->
<interceptors>
<!--注册拦截器-->
<interceptor name="loginInterceptor" class="com.bfchengnuo.interceptor.LoginInterceptor" />

<!--配置自定义拦截器栈-->
<interceptor-stack name="loginStack">
<interceptor-ref name="loginInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>

<!--可以定义默认的栈(所有的 Action 都执行这个默认的栈)-->
<default-interceptor-ref name="loginStack"/>

<!--定义全局的返回结果-->
<!--<global-results>-->
<!--<result name="login">/login.jsp</result>-->
<!--</global-results>-->

<action name="Login_*" class="com.bfchengnuo.interceptor.LoginAction" method="{1}">
<result>/reg/success.jsp</result>
<result name="login">/login.jsp</result>
<result name="toLoginView">/login.jsp</result>
</action>
</package>

嗯….这样应该就差不多了吧….
需要注意的是:配置自定义拦截器(interceptors)要放在 package 中的最前面,如果使用 default-interceptor-ref 那么所有的 Action 都会执行其中定义的栈,如果只需要特定的 Action 执行自定义的栈,可以在相应的 Action 中进行配置,这样其他的 Action 就还是执行默认的栈。

OGNL表达式

OGNL 也是一个开源的项目,Struts2 融合了 OGNL 并且将它作为默认的表达式语言,它必须依赖于 Struts 的标签才能使用
OGNL 由表达式语言和类型转换器组成,就是为了方便数据的访问;总体来说和 EL 表达式是很像的,并且兼容 EL 表达式,大多数表达式不需要进行转义,如果用到了使用 %{xxx} 进行转义,也叫做强制表达式解析(在不自动进行 OGNL 解析的地方强制解析),多用于非 String 对象
举个简单使用的栗子,首先是主要的 JSP 页面部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<s:form namespace="/ognl" action="OgnlAction" method="POST">
<s:textfield name="names[0]" label="names[0]"/>
<s:textfield name="names[1]" label="names[1]"/>
<%--<s:textfield name="names" label="names"/>--%>
<s:textfield name="nameList[0].name" label="nameList-name"/>
<s:textfield name="nameList[0].age" label="nameList-age"/>
<s:textfield name="user" label="user-conver"/>

<s:textfield name="nameMap.key.name" label="Map-name"/>
<s:submit/>
</s:form>
</body>

在测试的时候还掉进了一个坑,就上面所示: 如果直接写 names 默认会存一个值(数组大小也被置为1),但是不要 names 和 names[0] 同时出现,这样会被覆盖,当时就被坑了好久…..竟然犯这样的低级错误
数组、集合都可以和 EL 一样使用,属性链也 OK;当然 map 也是可以的:
map 的 key 如果是字符串可以直接用 . 连接:map.key.name ;否则就使用 map['key'].name 这样的形式
我是把把表单提交后的页面再指向回此 JSP ,这样数据会回显,以此来观察效果

相关的 Action 没啥好说的,就是定义上面所述的属性,List、Map 可以不指定泛型,在 OgnlAction-conversion.properties Action 相关文件中指定,如果指定了泛型,那么就会自动进行转换的,转换并不只是八种基本类型,还要强大些,关于 conversion 文件,这是指定前台提交的数据如何进行转换的,毕竟前台提交的都是 String:

1
2
3
4
5
6
7
8
9
# 指定 List 内装的是什么类型
Element_nameList=com.bfchengnuo.domain.User
Element_nameMap=com.bfchengnuo.domain.User

# 还可以定义 Map 指定类型的 key
#Key_nameMap=java.lang.Integer

# 指定转换器
user=com.bfchengnuo.ognl.UserConverter

通过这个文件就可以确定前面表单中的 nameList[0].name 指的就是 List 中第一个 user 对象中的 name 属性;
表单中还有一个 name 叫 user 的,这个是我编写的一个简单的 javabean,正常来说需要这样写:user.name 才能正确的赋值,总不能把一个字符串赋值给一个 user 对象吧,如果硬要这样那就必须定义一个转换器了,就是上面 properties 文件指定的那个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class UserConverter extends StrutsTypeConverter {
// 字符串转成对象,客户端提交时
@Override
public Object convertFromString(Map map, String[] strings, Class aClass) {
if (strings != null && strings.length > 0) {
String[] ss = strings[0].split("-");
if (ss.length > 0) {
User user = new User();
user.setName(ss[0]);
user.setAge(ss[1]);
return user;
}
}
return null;
}

// 对象转成对象,服务器返回时
@Override
public String convertToString(Map map, Object o) {
// 直接调用自定义的 tostring 方法
return o.toString();
}
}

定义转换器需要继承 StrutsTypeConverter ,复写两个方法,一个是用于客户端提交数据时转换,一个是服务器返回数据回显时转换;它和值栈挨着,提交数据时经过转换后存到值栈,回显数据时经过转换输出到客户端

对于转换器来说,每个 Action 都搞一个太累,可以在 src 根目录下定义 xwork-conversion.property 全局的类型转换器

OGNL中重要的3个符号

#%$ 符号在 OGNL 表达式中经常出现,他们的意义嘛

  • #
    它的用途一般有三种,最常用的就是用来访问非根对象属性 ,比如:#session/#request/#application 等;# 相当于 ActionContext.getContext() 默认会自动搜索各个域
    第二种是用于过滤和投影(projecting)集合
    第三种是用来构造集合,Map 和 List :#{key1:val1,key2:val2}/{1,2,3}
  • %
    % 符号的用途是在标志的属性为字符串类型时(或者说值类型的标签中),计算 OGNL 表达式的值,相当于提供了一个 OGNL 的解析环境,就是所谓的强制(暴力)解析了;
    如果本来默认解析 OGNL 的标签(对象类型标签)想让其单纯的输出值,就需要在双引号里加一层单引号
  • $
    主要用途有两个:在国际化资源文件中,引用 OGNL 表达式;在 Struts2 配置文件中,引用 OGNL 表达式
    比如在使用重定向时,传参只能通过 url;通过这个就可以获得 Action 中的数据

关于值栈

OGNL 和值栈有密切的关系,既然用户要获取一些必要的数据(JSP 中)那么怎么才能把 Action 中的数据传给用户呢,就是用的值栈
Action 创建的同时也会创建值栈以及 ActionContext,然后会把 Action 对象封装进值栈中(在根元素),通过 Request 传给用户(key 为 struts.ValueStak);获取值栈可以通过 ActionContext,也可以通过 Request
值栈当中维护了一个 OgnlContext 对象(request、session 等对象的数据就存在这里,Action 对象在根元素),就是 OGNL 表达式所用的,其实是一个 Map
从上面可以看出,Action 是放进了值栈的根节点,那么 Action 中的属性也毫无疑问的放在了根节点,而 OGNL 表达式取根节点的值是不需要加 # 符号的,所以可以使用 # + 属性名 直接取

OGNL标签

如同 JSTL ,OGNL 也自带了一些标签供使用;首先还要明确一点:
struts2 接收到请求后马上创建一个 ActionContext,一个 ValueStak 和一个 Action 对象;Action 对象立即放到值栈上便于 OGNL 的访问
顶层对象会覆盖底层对象,也就说从上往下找,找到了就不会继续往下了
默认是从栈顶的对象里寻找,是栈顶对象!所以,一般是 Action 在栈顶;但是如果 user 对象在栈顶(前面所说的模型驱动),可以直接用其属性
OGNL 不仅可以访问 bean 的属性,还可以访问方法,但是不建议用

Struts2 的标签库都是使用 OGNL 表达式来访问 ActionContext 中的对象数据的;并且将 ActionContext 设置为 OGNL 上下文,默认的根对象为值栈

s:property

首先是有关获取数据的几个标签,也就 ActionContext 中的六个“域”,我事先在 Action 初始化了一波数据,后面会用到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public String execute() {
initData();
return SUCCESS;
}

private void initData() {
// 往各个域中存储数据
ServletActionContext.getRequest().setAttribute("name","request-name");
if (ServletActionContext.getRequest().getSession() == null) {
System.out.println("session--Null");
}
ServletActionContext.getRequest().getSession().setAttribute("name","session-name");
ServletActionContext.getServletContext().setAttribute("name","application-name");
ServletActionContext.getContext().getValueStack().set("name","vs-name");

popList();
}

// 填充 List
private void popList() {
nameList = new ArrayList();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setName("Loli-No" + i);
user.setAge((i+8) + "");
nameList.add(user);
}
}

然后在 JSP 页面中直接获取就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<s:debug/> <br>
request.name:<s:property value="#request.name"/> <br>
session.name:<s:property value="#session.name"/> <br>
application.name:<s:property value="#application.name"/> <br>
<%-- 从小到大依次搜索 --%>
attr.name:<s:property value="#attr.name"/> <br>
parameters.name:<s:property value="#parameters.name"/> <br>
<%-- 值栈 --%>
vs.name:<s:property value="name"/> <br>
强制表达式解析: <s:textfield label="%{#request.name}"/> <br>
<%-- 使用单引号输出常量 --%>
输出常量:<s:property value="'is constant'"/> <br>
<%-- 关闭自动转义 --%>
<s:property value="'<h1>取消了转义</h1>'" escape="false"/> <br>
</body>

强制表达式解析前面已经提过了,就是用在默认不会进行 OGNL 解析的地方,让其强制进行解析;
还有那个输出常量,加单引号,就是不让其进行 OGNL 解析,但是内容会被 Html 编码,这里我就被坑了…..请记得:在默认进行解析的属性中,如果不使用 OGNL 的时候记得加单引号!

s:set

它是用来存储数据的,并且可以存储到指定的域中;如果没有指定范围则默认保存在 ActionContext 的大 Map 中,可以使用 #name 来获取,也可以不加 # ,那样就会先从值栈中搜寻,找不到了再去大 Map 中去找

1
2
<s:set var="s_setName" value="'loli'"/>
<s:property value="#s_setName"/> <br>

无论是从那个域取数据,如果找不到最终都会到这个大 Map 自身中去找,
当然还可以存取 List:<s:set name="miloList" value="{‘java’,’php’,’C#’}"/>
# 可以理解为是 ActionContext.getContext()

s:push

同样是用来存储数据的,将对象放到值栈的栈顶,标签结束后会自动删除,不过不能指定存储位置,必须是值栈

1
2
3
<s:push value="'Lolicon'">
<s:property/>
</s:push>

property 不指定 value 的话会自动获取栈顶的对象(值栈),并且会调用其的 toString 方法;另外,不使用 OGNL 的话千万记得加单引号!

s:bean

创建新的 javabean 到栈顶(说的当然是值栈);如果指定了 var 属性,同时还会保存引用到 ActionContext 中,如果不指定,在标签结束后从值栈移除引用后就无法取得数据了

1
2
3
4
5
6
7
<s:bean name="com.bfchengnuo.domain.User" var="mybean">
<%-- 属性赋值 --%>
<s:param name="name" value="'小奏'"/>
<s:property value="name"/>
</s:bean>
<br>
存储的 name 为(ActionContext 中):<s:property value="#mybean.name"/>

在 s:bean 标签中的 property 可以直接写 属性名 获取数据….

s:action

作用就是在 JSP 中直接调用某个 Action,executeResult 属性可以指定是否把页面包含进来

1
2
3
4
5
<s:action name="HelloWorldAction" namespace="/one" executeResult="true">
<s:param name="user.name" value="'Lolicon'"/>
<s:param name="name" value="'Loli'"/>
<s:param name="age" value="'14'"/>
</s:action>

在调用的时候我还传了几个参数过去…..
在调用 Action 的时候是在一个线程中,也是一个栈中,后来的 Action 在栈顶 (HelloWorldAction)

s:iterator

迭代器….已经很熟了吧,在 JSTL、Struts1 中都见过,就是用来迭代集合的,包括 Map;Struts2 中的迭代器也很好用,非常强大
每次迭代都会把数据放在栈顶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<table border="0" cellspacing="1" bgcolor="#db7093">
<tr>
<td>姓名</td>
<td>年龄</td>
<td>编号</td>
<td>索引</td>
<td>是否偶数</td>
</tr>
<s:iterator value="nameList" status="st">
<tr class="<s:property value="#st.even?'even':'odd'"/>">
<td><s:property value="name"/></td>
<td><s:property value="age"/></td>
<td><s:property value="#st.count"/></td>
<td><s:property value="#st.index"/></td>
<td><s:property value="#st.even"/></td>
<%-- 状态还可以是 isFirst isLast--%>
</tr>
</s:iterator>
</table>

status 这个对象确实能省不少事呢

s:if/s:elseif/s:else

这个标签就爽了,相比 JSTL 里的简直太爽,可想像在写 java 一样

1
2
3
4
5
<s:set var="age" value="14"/>
age:<s:property value="#age"/> <br>
<s:if test="#age < 10">小于10</s:if>
<s:elseif test="#age > 20">大于20</s:elseif>
<s:else>10-20</s:else>

以后做逻辑判断就简单多了
需要注意的是,第一行代码的 set 没有用把数字放单引号里,这说明它是一个整型,在使用的时候记得加 #

s:url

在前面的标签库里也见过,就是用来构件 url 的,避免重复写那繁琐的代码,同样;如果加 var 属性就会存到大 Map 中去;最简单的:<s:url /> 表示当前的地址

1
2
3
4
5
6
 当前地址(带参数):<s:url includeParams="all"/> <br>
<s:url action="WaitAction" namespace="/reg" var="waitUrl">
<%-- 如果有参数的话可以这样赋值 --%>
<%--<s:param name="key" value="lalala"/>--%>
</s:url>
<a href="<s:property value="#waitUrl"/>">跳转等待页</a>

除了访问 Action 可以直接在 value 属性里写 http 地址

其他

在迭代的时候,可以快速的定义集合,使用 {}

1
2
3
<s:iterator value="{'loli','2','ll','gt'}">
<s:property /> <br>
</s:iterator>

对于 Map,也差不多,不过需要加 # ,也就是这样:#{'key':'val','key2':'val'}
使用 property 标签取的时候直接 value=key 、 value=value 这样就行了,因为放在栈顶的就是 Entry 啊,其中就有 getKey、getValue 这样的方法啊


为了证明可以调用方法,可以这样调用看看:

1
2
3
nameList.size: <s:property value="nameList.size"/> <br>
nameList.isEmpty: <s:property value="nameList.isEmpty"/> <br>
nameList.isEmpty: <s:property value="methodName('para')"/>

如果需要调用静态内容,需要指定包名,类似:@java.util.Locale@CHINA
调用静态对象的有参方法:@[email protected](@java.util.locale@CHINA)
注意:静态方法使用 @ 连接,如果非静态用 . 就可以了,上面栗子是调用的静态对象 CHINA 中的非静态方法 getDisplayName
由于 Math 比较常用,默认是可以省略的,比如:@@floor(10.09)


除此之外,还有一些 UI 标签,比如上面用到的 <s:form action=''> ,总的来说其实和 html 标签并没有什么区别,并且应该是更方便的,上面的这个 form 标签直接在 action 中填 /actionName 就好了,不需要那一串了
但是在使用 s:a 标签的时候地址写上 / 反而不正确了,而上面的 form 标签就不会;至于到底加不加 / 最好的方法就是试一下了…..emmmm
使用这些标签的时候总是会带一些比较迷的样式(虽然可以设置 theme 属性为 simple),所以一般情况下这类标签是不用的,除非用到了其特别的功能

<s:textfield name='' /> 标签会自动回显数据,也就是会自动根据 name 属性来从值栈(只有根元素中才是栈,其他的域都是在 Map 中)获取相应的值,某些时候,只需要在值栈中 push 相应的数据就可以了


有一点还是在这里写下吧,大概:
OGNL 中还可以使用类似 [1].user.name 来获取数据,意思就是栈中第二个对象的 user 对象的 name 属性

OGNL过滤和投影

不想说太多,因为我还没看到….

  • ?:选择满足条件的所有元素
  • ^:选择满足条件的第一个元素
  • $:选择满足条件的最后一个元素

语法为:collection.{?expression}collection.{^expression}collection.{$expression};栗子大概是这样的:

1
2
3
<s:property value="array.{?#this > 5}"/>
<s:property value="array.{^#this > 5}"/>
<s:property value="array.{$#this > 5}"/>

如果把集合中的数据想象成是数据库表中的数据,投影简单说就是从这表中选取某一列所构成的一个新的集合,感觉和 VO 差不多;投影的语法:collection.{expression} ;比如: <s:property value="personList.{name}"/>

多数情况下,过滤和投影是联合起来用的,使用起来还算简单:

1
2
3
<s:property value="personList.{?#this.sex.equals('female')}.{name}"/>
<s:property value="personList.{^#this.sex.equals('female')}.{name}"/>
<s:property value="personList.{$#this.sex.equals('female')}.{name}"/>

使用注解

如果厌倦了在配置文件中写一大堆的配置,可以选择使用注解,当然有利有弊,在前面的文章已经说过,下面来看看常用的注解:
另:使用注解需要 struts2-convention-plugin 的 jar 包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@ParentPackage("struts-default")
@Namespace("/test")
@Results( { @Result(name = "success", location = "/main.jsp"),
@Result(name = "error", location = "/error.jsp") })
@ExceptionMappings( { @ExceptionMapping(exception = "java.lange.RuntimeException", result = "error") })
public class LoginAction extends ActionSupport{
...
@Action("login1")
public String test1() throws Exception{...}

@Action( //表示请求的Action及处理方法
value="login2", //表示action的请求名称
results={ //表示结果跳转
@Result(name="success",location="/success.jsp",type="redirect"),
@Result(name="login",location="/login.jsp",type="redirect"),
@Result(name="error",location="/error.jsp",type="redirect")
},
interceptorRefs={ //表示拦截器引用
@InterceptorRef("defaultStack"),
@InterceptorRef("timer")
},
exceptionMappings={ //映射映射声明
@ExceptionMapping(exception="java.lang.Exception",result="error")
}
)
public String test1() throws Exception{...}
}

大体就是这样使用了….. @Action(url) 中的url,如果以 / 开头那是绝对路径,访问的时候不需要加命名空间,否则就要加命名空间,应该是吧,未测试

常用的注解:

Namespace:指定命名空间。
ParentPackage:指定父包。
Result:提供了Action结果的映射。(一个结果的映射)
Results:“Result”注解列表
ResultPath:指定结果页面的基路径。
Action:指定Action的访问URL。
Actions:“Action”注解列表。
ExceptionMapping:指定异常映射。(映射一个声明异常)
ExceptionMappings:一级声明异常的数组。
InterceptorRef:拦截器引用。
InterceptorRefs:拦截器引用组。

其他的一些用到再补充吧,比如那些拦截注解 Before 、After 之类的,类型转换注解啊。。。总之;注解能做很多事

其他补充

当我们需要把返回的内容转成 JSON 时,可以使用 Struts 提供的插件完成,导包就不说了(struts2-json-plugin)
配置文件中的 package 继承 json-default,返回值的 type 填 json ;
在 Action 中对应的方法中,把相应的数据保存到集合里,通常使用 List,这个集合要是全局的才行,因为要提供 getter/setter 方法,这样 Struts 就会自动进行处理了

参考

http://wiki.jikexueyuan.com/project/struts-2/annotations-types.html
http://www.blogjava.net/fancydeepin/archive/2014/03/17/struts-ognl.html

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~