struts2总结(五)--interceptor

8 拦截器(interceptor)

8.1 Struts架构图

Alt text

8.2 Struts执行过程分析

Alt text

  • 拦截器:拦截器是struts2框架中提供的一种Java类
  • 作用:
    * 可以拦截访问Action的请求
    * 给这个Action加上新的丰富功能(上传,参数自动接收,类型自动转换等)需要配置之后,指明哪一个拦截器去拦截哪一个Action或者哪一些Action,这个拦截器才会去拦截我们的Action。
    
  • 拦截器工作原理:
    1. 有一个拦截器的类(框架的或者自定义的)
    2. 在配置文件中把这个拦截器类配置出来
    3. 指明这个拦截器要拦截哪一个或者哪一些action
    4. 客户端发送一个请求访问一个被拦截器拦截的Action
    5. 这个请求首先会被struts2 的filter所拦截,filter会检查这个请求是不是请求的Action,如果是,会再检查这个Action有没有被定义的拦截器所拦截,如果有,那么把这个请求交给拦截器去处理
    

8.3 Interceptor拦截器过程模拟

  • 定义一个Interceptor接口,有一个intercept方法。
1
2
3
public interface Inteceptor {
public void intercept(ActionInvocation actionInvocation);
}
  • 创建两个interceptor实现了interceptor接口:
1
2
3
4
5
6
7
8
public class FirstInterceptor implements Inteceptor{
@Override
public void intercept(ActionInvocation actionInvocation) {
System.out.println(1);
actionInvocation.invoke();
System.out.println(-1);
}
}
1
2
3
4
5
6
7
8
public class SecondInterceptor implements Inteceptor{
@Override
public void intercept(ActionInvocation actionInvocation) {
System.out.println(2);
actionInvocation.invoke();
System.out.println(-2);
}
}
  • 定义一个Action
1
2
3
4
5
6
7
8
public class SecondInterceptor implements Inteceptor{
@Override
public void intercept(ActionInvocation actionInvocation) {
System.out.println(2);
actionInvocation.invoke();
System.out.println(-2);
}
}
  • 定义ActionInvocation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.ArrayList;
import java.util.List;
public class ActionInvocation {
List<Inteceptor> inteceptors = new ArrayList<>();
int index = -1;
Action a = new Action();
public ActionInvocation() {
this.inteceptors.add(new FirstInterceptor());
this.inteceptors.add(new SecondInterceptor());
}
public void invoke() {
index++;
if (index >= this.inteceptors.size()) {
a.execute();
} else {
this.inteceptors.get(index).intercept(this);
}
}
}
  • 执行main文件:
1
2
3
4
5
public class Main {
public static void main(String[] args) {
new ActionInvocation().invoke();
}
}
  • 运行结果为:
1
2
3
4
5
1
2
execute
-2
-1

从而执行顺序为,首先main函数,创建ActionInvocation对象,初始化的时候讲两个Inteceptor加到interceptors这个列表中,调用其invoke()方法,当还有interceptor没有访问时,调用第一个interceptor的intercept方法,输出1,并调用ActionInvocation的invoke方法,此时会调用第二个Inteceptor的intercept方法,输出2,并调用ActionInvocation的invoke方法,此时不存在拦截器,执行Action输出execute,后面依次输出-2 -1, 完成这个模拟过程!

8.4 定义自己的拦截器

  • struts2框架已经写好了很多个拦截器,同时也把这些拦截器配置在配置文件里面struts-default.xml中,除此之外,我们可以写自己的拦截器

  • 首先实现一个Interceptor接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyInterceptor implements Interceptor {
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation invocation) throws Exception {
long start = System.currentTimeMillis();
String r = invocation.invoke();
long end = System.currentTimeMillis();
System.out.println("action time = " + (end - start));
return r;
}
}
  • 然后在struts.xml文件中配置出这个拦截器:
1
2
3
4
5
6
7
8
9
10
11
12
<package name="test" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="my" class="com.interceptor.MyInterceptor"></interceptor>
</interceptors>
<action name="test" class="com.action.TestAction">
<result>/test.jsp</result>
<interceptor-ref name="my"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>

8.5 拦截器栈

当一个Action需要被多个拦截器拦截的时候,正常情况下我们需要在这个Action中去引用要使用的多个拦截器,但是我们可以使用一个拦截器栈去包含那几个拦截器,然后直接在Action中引用这个拦截器栈就可以。

  • 一个拦截器栈可以包含多个拦截器
  • 一个拦截器栈还可以包含其他拦截器栈
  • 定义拦截器栈都要在标签中
1
2
3
4
5
6
7
8
9
10
11
<interceptors>
<interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor>
<interceptor-stack name="myStack">
<!-- 这是我们自己定义的一个拦截器 -->
<interceptor-ref name="myInterceptor"></interceptor-ref>
<!-- 这是struts-default.xml文件中定义的一个拦截器 -->
<interceptor-ref name="params"></interceptor-ref>
<!-- 这是struts-default.xml文件中定义的一个拦截器栈 -->
<interceptor-ref name="basicStack"></interceptor-ref>
</interceptor-stack>
</interceptors>

8.6 默认拦截器栈/拦截器

在一个package中,我们可以把一个拦截器或者拦截器栈的声明为一个默认的拦截器/拦截器栈。以后这个package中所有的Action都会被这个默认的拦截器/拦截器栈所拦截

  • 我们写的任何Action都会被一个叫做defaultStack的拦截器栈所拦截,这个拦截器栈包含了十几个拦截器,这些拦截器给我们的Action提供了很多丰富的功能,因为我们写的所有的package都是直接或者间接继承了struts-default.xml文件中的一个名字叫struts-default的package, struts-default包中又把名字叫做defaultStack的拦截器栈配置成了一个默认的拦截器栈,我们的package继承了这个,所有的Action都会被defaultStack所拦截。
  • 但是如果我们指明了一个Action被我们所写的一个拦截器/拦截器栈所拦截,name这个Action就不会被defaultStack拦截了,所以我们需要在Action中主动的在声明这个Action被defaultStack所拦截,或者把defaultStack加入到我们自己定义的拦截器栈里面。
  • 也可以专门定义一个package专门定义拦截器,拦截器栈,其他package继承这个拦截器栈package:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 在这个package中,我们只定义拦截器/拦截器栈 -->
<package name="MyInter" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="myInterceptor" class="com.briup.web.interceptor.MyInterceptor"></interceptor>
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<!-- 这是struts-default.xml文件中定义的一个拦截器栈 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 声明默认拦截器/拦截器栈 -->
<!-- 当前包中所有的action都会被这个myStack所拦截器 -->
<!-- 继承了当前包的其他包里面的所有action也会被这个myStack所拦截器 -->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
</package>

8.7 类型转换

类型转换:解析HTTP请求参数,将HTTP请求参数赋值给Action属性,当其类型不一致时进行的类型转换操作。struts2完成了String和基本类型的类型转换,只要Action属性有get set 方法。同时其还支持自定义的类型转换。

struts2类型转换时通过Params拦截器进行转换的;如果转换失败,则conversionError拦截器拦截该异常,并封装到fieldError中,放入ActionContext中。

8.7.1 基础类型转换:

testAction:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class TestAction extends ActionSupport{
private String name;
private int age;
private Date d;
Set<String> interests;
Map<String, String> users;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getD() {
return d;
}
public void setD(Date d) {
this.d = d;
}
public Set<String> getInterests() {
return interests;
}
public void setInterests(Set<String> interests) {
this.interests = interests;
}
public Map<String, String> getUsers() {
return users;
}
public void setUsers(Map<String, String> users) {
this.users = users;
}
@Override
public String execute() throws Exception {
return super.execute();
}
}

test.jsp:

1
2
3
4
5
6
7
8
<body>
name:<s:property value="name"/><br/>
age:<s:property value="age"/><br/>
date:<s:property value="d"/><br/>
<s:date name="d" format="yyyy/MM/dd HH:mm:ss"/><br/>
<s:property value="interests"/><br/>
<s:property value="users"/><br/>
</body>

8.7.2自定义类型转换

自定义类型转换器方法:

继承DefaultTypeConverter,重写convertValue方法,这个方法的功能是完成双向转换,Sting数组转换到Action属性。函数模板为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyPointConverter extends DefaultTypeConverter{
@Override
public Object convertValue(Object value, Class toType) {
if(toType == Point.class) {
Point p = new Point();
String[] strs = (String[])value;
String[] xy = strs[0].split(",");
p.x = Integer.parseInt(xy[0]);
p.y = Integer.parseInt(xy[1]);
return p;
}
if(toType == String.class) {
return value.toString();
}
return super.convertValue(value, toType);
}
}
继承StrutsTypeConverter
  • StrutsTypeConverter是DefaultTypeConverter的子类。在两个方向的类型转换上分别实现:

MyPointerConverter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyPointConverter extends StrutsTypeConverter{
@Override
public Object convertFromString(Map map, String[] strings, Class aClass) {
Point point = new Point();
String[] strings1 = (String[])strings;
String[] xy = strings1[0].split(",");
point.x = Integer.parseInt(xy[0]);
point.y = Integer.parseInt(xy[1]);
return point;
}
@Override
public String convertToString(Map map, Object o) {
return null;
}
}

TestAction:

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 TestAction extends ActionSupport{
Point p;
List<Point> ps;
Map<String, Point> points;
public Point getP() {
return p;
}
public void setP(Point p) {
this.p = p;
}
public List<Point> getPs() {
return ps;
}
public void setPs(List<Point> ps) {
this.ps = ps;
}
public Map<String, Point> getPoints() {
return points;
}
public void setPoints(Map<String, Point> points) {
this.points = points;
}
@Override
public String execute() throws Exception {
return super.execute();
}
}

jsp:

1
2
3
4
5
<body>
<s:property value="p"/><br/>
<s:property value="ps"/><br/>
points:<s:property value="points"/><br/>
</body>
注册方式:
  • 第一种配置方法为在具备类型转换文件中配置,局部类型转换文件命名为ActionName-conversion.properties,位置放在特定的Action目录下,文件内容格式为:typeName=ConverClass仅针对特定的Action的特定属性有效
  • 第二种配置方法为全局类型转换文件中红配置,文件名为xwork-conversion.properties,在WEB-INF\classes下,文件内容格式为:attributeName=ConvertClass, 对某个类型都有效,比如对Point类型注册了类型转换器。
  • 注解

    8.8 拦截器和过滤器的比较

  • 相同点:
    • 都是一种Java类
    • 都能拦截客户端发给服务端的请求
    • 拦截到请求之后都可以做一些相应的处理,最后还可以把这个请求放行
    • 都需要实现各自相应的接口记忆在相应的配置文件中配置
  • 不同点:

    • 拦截器是Struts2框架中定义的,过滤器是web里面的对象,是J2EE标准定义的
    • 拦截器只会拦截访问Action的请求,过滤器可以拦截所有的请求
    • 拦截器定义在struts.xml文件中,过滤器定义在web.xml中
    • 拦截器对象的创建、调用、销毁时struts2框架负责的,过滤器对象的创建、调用、销毁时服务器负责的。

    我们自己定义的filter也可以拦截struts2框架中的Action,需要在web.xml文件中把我们自己的filter配置在struts2的filter上面才可以,因为web.xml文件中的filter配置的先后顺序控制filter起作用的顺序,如果struts的filter先拦截到访问action的请求后,不会把这个请求交给下面的filter,而是交给它内部的拦截器。如果我们自己的filter拦截到请求之后,还是会交给下一个filter,也就是交给struts2的filter

9 注解Annotation

9.1 注解使用方式

  1. 引入支持struts2框架注解开发的jar包 struts2-convention-plugin-2.3.4.1
  2. struts.xml <constant name="struts.convention.action.suffix" value="Action"/>
  3. Struts2使用注解开发需要遵循一定的规范:

    • Action要必须继承ActionSupport父类
    • Action所在的包名必须以Action结尾
    • package-info.java 在这里配置这个包下所有的类都可以用
    1
    2
    3
    4
    5
    6
    @Namespace("/")
    @ParentPackage("struts-default")
    @DefaultInterceptorRef("authStack")//authStack自定义的拦截器
    package com.briup.action.manager;
    import org.apache.struts2.convention.annotation.Namespace;
    import org.apache.struts2.convention.annotation.ParentPackage;

9.2 Action常用注解

  • Namespace Annotation

    • 通过在ActionClass上定义@Namespace(“/custom”)
    • 通过package0info.java定义
      1
      2
      3
      4
      @Namespace("/manager")
      @ParentPackage("default")
      @DefaultInterceptorRef("authStack")
      package com.example.actions;
  • Action Annotation

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    1. @Action(interceptorRefs={
    @InterceptorRef("validation"),
    @InterceptorRef("defaultStack")
    })
    2. chain
    @Action("foo")
    public String foo() {
    return "bar";
    }
    @Action("foo-bar")
    public String bar() {
    return SUCCESS;
    }
  • Result Annotation

    • 全局, 整个类可以访问
    • 局部, 某个方法可以访问
      1
      2
      3
      4
      5
      6
      7
      8
      9
      @Results({
      @Result(name="failure", location="fail.jsp")
      })
      public class HelloWorld extends ActionSupport {
      @Action(value="/different/url",results={@Result(name="success", location="http://struts.apache.org", type="redirect")} )
      public String execute() {
      return SUCCESS;
      }
      }

    参考:http://www.jianshu.com/p/a884504a8863

Donate comment here