本章重点描述了一下struts拦截器的使用,文章的前面顺带介绍了一下struts的Bean配置、常量配置和包配置
1、Bean配置
Struts2是一个高度可拓展的框架,框架大部分核心组件都是以配置的方式写在配置文件中的,当开发者需要替换其核心组件的时候,只需要写好自己的实现类,
然后配置到配置文件中。我们打开struts2-core-2.x.x.jar文件,打开里面的struts-default.xml文件,看到该文件配置了大量的Bean定义,该配置文件部分
代码如下所示:
上面配置文件中配置了Struts2的核心的Bean。在struts.xml文件中Bean通常有两个作用:
(1)创建该实例Bean,作为struts2的核心Bean。
(2)Bean包含的一些静态方法需要值传入。
注意,对绝大多数Struts2的应用而言,无需重新定义Struts2内的核心组件,也无需在struts.xml中配置Bean标签。
2、常量配置
前面说了,这块可以在struts.xml中配置,也可以在struts.properties中配置,其实还可以在web.xml加载FilterDispatcher的时候加载常量参数。
在struts.xml中配置的方式:
3、包配置
Struts2的核心就是Action、拦截器,Struts2使用包来管理Action和拦截器。每个包下就是多个Action、多个拦截器、多个拦截器的引用的集合。
配置包时必须指定name属性,除此之外还可以指定一个可选属性extends,extends属性值必须是另一个包的name属性的值,使用exdends属性的目的是继承
其他包,继承父包中的Action、拦截器等配置。
除此之外,Struts2还提供了一种抽象包,抽象包的含义是该包不能有Action的定义。为了显示指定一个抽象包,必须将package元素的abstract属性设置成
true。
namespase用来指定命名空间是一个可选属性。
考虑到同一个应用可能会用到同名的Action,Struts2以命名空间的方式来管理它们,同一个命名空间下不能有相同的Action,不同的命名空间下可以有相同的
Action。
Struts2不支持为单独的Action设置命名空间,而是通过为package指定namespase属性来为该包下的所有Action统一指定命名空间。
/error.jsp
/welcome.jsp
/login.jsp
对于上面的这个配置文件,配置了两个包,一个是org一个是get,其中get包设置了namespase属性/apple,对于org包而言,没有设置namespase属性。
对于没有配置namespase属性的包,其为默认包,默认的namespase=" "
当为某个包配置了namespase属性的时候,那么访问的时候就需要是namespase的值加上Action的值。访问路径:
/**
其中strutsDemo是项目名称,apple是命名空间,login是Action的name
*/
http://localhost:8080/strutsDemo/apple/login
除此之外,struts还可以通过namespase="/"来指定根命名空间。
如果Url为/apple/login 那么系统首先会在命名空间为apple的包下寻找name值为login的Action,如果该包下找到了那么就处理请求,如果没有找到,继续在
默认命名空间中寻找,如果找到则处理请求,如果还没找到,那么抛出异常。
如果Url为/login,则首先会在根命名空间中寻找,其次去默认命名空间中寻找,如果两者都没有找到,抛出异常。
4、拦截器的配置
这部分很重要!!!
(1)理解拦截器
struts2拦截器是在访问某个action或者某个action方法的,访问之前或者之后实施拦截,并且Struts2拦截器是可拔插的,拦截器是AOP的一种实现。
拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序连结成一条链。在访问被拦截的方法或者字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
(2)实现Struts2拦截器原理
Struts2拦截器的实现原理相对简单,当请求struts2的action时,struts2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串联成一个列表,最后一个一个的调用列表中的拦截器。
………………………………………………………………………………………………………………………………………………
……………………………………………………………………………………………………
还可以为拦截器设置参数:
参数值
参数值
(4)如何使用拦截器栈
一个拦截器栈可以包括一个或者多个拦截器,也可以包括其他拦截器栈。
………………………………………………………………………………………………………………………………………………
为Action应用拦截器栈的方式和应用拦截器一样。
(5)自定义拦截器
自定义拦截器有2种实现方式,一种是实现Interceptor接口,另一种是继承AbstractInterceptor类。
看下实现Inteceptor接口的示例:
package wwfy.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
@SuppressWarnings("serial")
public class CustomInterceptor1 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 {
System.out.println("--------拦截器开始----------");
String result = invocation.invoke();
System.out.println("--------拦截器结束----------");
return result;
}
}
看下继承AbstractInterceptor类的实例:
package wwfy.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
@SuppressWarnings("serial")
public class CustomInterceptor2 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("--------拦截器开始----------");
String result = invocation.invoke();
System.out.println("--------拦截器结束----------");
return result;
}
}
invocation.invoke()的作用:
在这个实现类中,实际上已经实现了最简单的拦截器的雏形。或许大家对这样的代码还比较陌生,这没有关系。我在这里需要指出的是一个很重要的方法
invocation.invoke()。
这是ActionInvocation中的方法,而ActionInvocation是Action调度者,所以这个方法具备以下2层含义:
如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。
如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action执行。
所以,我们可以发现,invocation.invoke()这个方法其实是整个拦截器框架的实现核心。基于这样的实现机制,我们还可以得到下面2个非常重要的推论:
如果在拦截器中,我们不使用invocation.invoke()来完成堆栈中下一个元素的调用,而是直接返回一个字符串作为执行结果,那么整个执行将被中止。
我们可以以invocation.invoke()为界,将拦截器中的代码分成2个部分,在invocation.invoke()之前的代码,将会在Action之前被依次执行,而在
invocation.invoke()之后的代码,将会在Action之后被逆序执行。
由此,我们就可以通过invocation.invoke()作为Action代码真正的拦截点,从而实现AOP。