struts2拦截器的结构图
自定义拦截器编写
定义一个拦截器
从上面的其结构图来看,我们要定义一个拦截器,只需继承AbstractInterceptor或者实现Interceptor接口,然后实现intercept方法
public class MyInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
System.out.println("自定义拦截器执行了");
return null;
}
}
现在创建了一个拦截器就可以使用了嘛,当然不是,
配置该拦截器使用
配置拦截器使用有两个步骤
第一:声明拦截器(在使用前一定要声明)]
<package name="p1" extends="struts-default" >
<!--声明拦截器 -->
<interceptors>
<interceptor name="myinterceptor" class="com.yu.web.action.MyInterceptor"></interceptor>
</interceptors>
第二步:使用拦截器
<action name="hello" class="com.yu.web.action.HelloAction" method="saveUser">
<!--使用拦截器,使用拦截器之前,必须声明它 -->
<!--在这里配置拦截器之后,默认配置的拦截器将会失效 -->
<interceptor-ref name="myinterceptor"></interceptor-ref>
<result>/success.jsp</result><!-- 当注册成功之后重定向的结果视图 -->
<result name="exists">/message.jsp</result><!-- 当用户名已经存在之后,转向的结果视图 -->
<result name="input">/register.jsp</result>
</action>
测试拦截器是否起作用,触发上面配置的action
而我们的动作方法是:
public String saveUser(){
System.out.println("动作方法执行了");
return SUCCESS;
}
所以看到,动作方法并没有执行,这是什么原因了,我们看struts2的结构流程图可知,动作方法执行之前是先执行其拦截器的,我们在前面的拦截器中只是打印了y一句话,并没有做其它操作,我们想要动作方法也执行,就必须放行此拦截器。
public class MyInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
System.out.println("自定义拦截器执行了");
String result=invocation.invoke(); //放行次拦截器向下执行
//执行完结果视图后,反向执行拦截器
System.out.println("反向执行");
//放行后结果返回的结果是结果视图的名称
System.out.println(result);
return null;
}
}
打印结果:
自定义拦截器执行了
动作方法执行了
反向执行
多个拦截器的执行顺序
创建一个同样的interceptor
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MyInterceptor2 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
System.out.println("自定义拦截器2执行了");
String result=invocation.invoke(); //放行次拦截器向下执行
//执行完结果视图后,反向执行拦截器
System.out.println("自定义拦截器2反向执行");
//放行后结果返回的结果是结果视图的名称
System.out.println(result+"视图结果");
return null;
}
}
声明和配置定义过滤器
<package name="p1" extends="struts-default" >
<!--声明拦截器 -->
<interceptors>
<interceptor name="myinterceptor1" class="com.yu.web.action.MyInterceptor1"></interceptor>
<interceptor name="myinterceptor2" class="com.yu.web.action.MyInterceptor2"></interceptor>
</interceptors>
<action name="findAll" class="com.yu.web.action.HelloAction" method="findAll">
<result>/findall.jsp</result>
</action>
<action name="hello" class="com.yu.web.action.HelloAction" method="saveUser">
<!--使用拦截器,使用拦截器之前,必须声明它 -->
<!--在这里配置拦截器之后,默认配置的拦截器将会失效 -->
<interceptor-ref name="myinterceptor1"></interceptor-ref>
<interceptor-ref name="myinterceptor2"></interceptor-ref>
<result>/success.jsp</result><!-- 当注册成功之后重定向的结果视图 -->
<result name="exists">/message.jsp</result><!-- 当用户名已经存在之后,转向的结果视图 -->
<result name="input">/register.jsp</result>
</action>
</package>
打印结果:
自定义拦截器1执行了
自定义拦截器2执行了
动作方法执行了
自定义拦截器2反向执行
自定义拦截器1反向执行
如果我们改变拦截器配置的顺序
<interceptor-ref name="myinterceptor2"></interceptor-ref>
<interceptor-ref name="myinterceptor1"></interceptor-ref>
打印结果:
自定义拦截器2执行了
自定义拦截器1执行了
动作方法执行了
自定义拦截器1反向执行
自定义拦截器2反向执行
所以从上面可以看到,拦截器的执行顺序和配置拦截器的顺序有关
intercept的返回值
其放行方法有个返回值,我们可以打印此值,看其结果.
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
System.out.println("自定义拦截器2执行了");
String result=invocation.invoke(); //放行次拦截器向下执行
//执行完结果视图后,反向执行拦截器
System.out.println("自定义拦截器2反向执行");
System.out.println(result);
return result;
}
打印结果:
自定义拦截器1执行了
动作方法执行了
jsp执行了
自定义拦截器1反向执行
success
自定义拦截器2反向执行
success
从上面可以看到,拦截器返回的就是其结果视图
拦截器的一个应用
在没有登录之前,不能查看其它页面。 那么就给其它页面一个检查是否已经登录的拦截器
拦截器:
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
//获取登陆标志
HttpSession session = ServletActionContext.getRequest().getSession();
//有user标志,表示已经登录
Object obj=session.getAttribute("user");
if(obj!=null){
String result= invocation.invoke();
return result;
}else{
//如果没有登录,则返回到登录页面
return "register";
}
}
拦截器配置:
<action name="findAll" class="com.yu.web.action.HelloAction" method="findAll">
<interceptor-ref name="myinterceptor1"></interceptor-ref>
<result>/findall.jsp</result>
<result name="register">/register.jsp</result>
</action>
触发此action,发现,如果没有登录标记,就会跳到登录页面.
先登录设置一个登录标记,检测是否可以进入其它页面
设置登录标记
public String saveUser(){
HttpSession session = ServletActionContext.getRequest().getSession();
session.setAttribute("user", "aaa");
System.out.println("动作方法执行了");
return SUCCESS;
}
现在可以进入其它页面
存在的问题
问题一:上述配置自己的拦截器后,默认的拦截器不起作用了,
a.解决办法:加入默认的拦截器
<action name="findAll" class="com.yu.web.action.HelloAction" method="findAll">
<!--为什么是defaultStack了,因为struts-default.xml文件中 所有默认拦截器的栈名是defaultStack -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="myinterceptor1"></interceptor-ref>
<result>/findall.jsp</result>
<result name="register">/register.jsp</result>
</action>
b.上述办法也会出现一个问题
当有拦截器,多个动作要写配置多个拦截器的时候,这样写比较麻烦,解决办法是可以,配置一个全局的拦截器栈
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.custom.i18n.resources" value="com.yu.web.resources.message"></constant>
<package name="mydefault" extends="struts-default">
<!--声明拦截器 -->
<interceptors>
<interceptor name="myinterceptor1" class="com.yu.web.action.MyInterceptor1"></interceptor>
<interceptor name="myinterceptor2" class="com.yu.web.action.MyInterceptor2"></interceptor>
<!--声明拦截器栈 -->
<interceptor-stack name="mydefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="myinterceptor1"></interceptor-ref>
</interceptor-stack>
</interceptors>
</package>
<!--注意:现在继承mydefault -->
<package name="p1" extends="mydefault" >
<action name="findAll" class="com.yu.web.action.HelloAction" method="findAll">
<!--为什么是defaultStack了,因为struts-default.xml文件中 所有默认拦截器的栈名是defaultStack -->
<interceptor-ref name="mydefaultStack"></interceptor-ref>
<result>/findall.jsp</result>
<result name="register">/register.jsp</result>
</action>
<action name="hello" class="com.yu.web.action.HelloAction" method="saveUser">
<result>/success.jsp</result><!-- 当注册成功之后重定向的结果视图 -->
<result name="exists">/message.jsp</result><!-- 当用户名已经存在之后,转向的结果视图 -->
<result name="input">/register.jsp</result>
</action>
</package>
</struts>
c.上述办法虽然解决了写多个拦截器的麻烦
但是仍然每个动作方法都要配置其拦截器,那有没直接配置一个就可以的了,当然,默认拦截器,我们并没有配置,但是执行每个动作方法前,都有被执行,其实,在struts-default.xml中,有配置默认的拦截器
<default-interceptor-ref name="defaultStack"/>
所以,我们也可以配置一个默认的拦截器
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.custom.i18n.resources" value="com.yu.web.resources.message"></constant>
<package name="mydefault" extends="struts-default">
<!--声明拦截器 -->
<interceptors>
<interceptor name="myinterceptor1" class="com.yu.web.action.MyInterceptor1"></interceptor>
<interceptor name="myinterceptor2" class="com.yu.web.action.MyInterceptor2"></interceptor>
<!--声明拦截器栈 -->
<interceptor-stack name="mydefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="myinterceptor1"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!--默认拦截器栈 -->
<default-interceptor-ref name="mydefaultStack"></default-interceptor-ref>
</package>
<!--注意:现在继承mydefault -->
<package name="p1" extends="mydefault" >
<action name="findAll" class="com.yu.web.action.HelloAction" method="findAll">
<result>/findall.jsp</result>
<result name="register">/register.jsp</result>
</action>
<action name="hello" class="com.yu.web.action.HelloAction" method="saveUser">
<result>/success.jsp</result><!-- 当注册成功之后重定向的结果视图 -->
<result name="exists">/message.jsp</result><!-- 当用户名已经存在之后,转向的结果视图 -->
<result name="input">/register.jsp</result>
</action>
</package>
</struts>
d.在使用了默认拦截器后,就给所有的动作方法都加上了拦截器
但是可能我们有些动作方法不需要加拦截器,这个怎么解决了
AbstractInterceptor有个子类,MethodFilterInterceptor,它有两个方法
public void setExcludeMethods(String excludeMethods) {
this.excludeMethods = TextParseUtil.commaDelimitedStringToSet(excludeMethods);
}
public void setIncludeMethods(String includeMethods) {
this.includeMethods = TextParseUtil.commaDelimitedStringToSet(includeMethods);
}
这两个方法表示,在一个拦截器中,可以配置那些方法不拦截,那些方法拦截
setIncludeMethods:表示拦截那些方法
setExcludeMethods:表示不拦截那些方法
首先:自定义拦截器继承MethodFilterInterceptor
public class MyInterceptor1 extends MethodFilterInterceptor {
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
//获取登陆标志
HttpSession session = ServletActionContext.getRequest().getSession();
//有user标志,表示已经登录
Object obj=session.getAttribute("user");
if(obj!=null){
String result= invocation.invoke();
return result;
}else{
//如果没有登录,则返回到登录页面
return "register";
}
}
}
为什么重写方法是doIntercept,难道是拦截器的方法改变了嘛,当然不是,拦截器执行的默认方法还是intercept(ActionInvocation invocation)这个方法,只是
MethodFilterInterceptor中intercept(ActionInvocation invocation)调用的是 doIntercept(ActionInvocation invocation)
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (applyInterceptor(invocation)) {
return doIntercept(invocation);
}
return invocation.invoke();
}
现在,我们配置我们要哪个方法需要拦截,哪个方法不需要,比如,我们在登录的时候,不需要检测用户是否登录
在申明拦截器栈的拦截器中配置(因为声明拦截器栈的过程中,相当于调用了拦截器)
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.custom.i18n.resources" value="com.yu.web.resources.message"></constant>
<package name="mydefault" extends="struts-default">
<!--声明拦截器 -->
<interceptors>
<interceptor name="myinterceptor1" class="com.yu.web.action.MyInterceptor1"></interceptor>
<interceptor name="myinterceptor2" class="com.yu.web.action.MyInterceptor2"></interceptor>
<!--声明拦截器栈 -->
<interceptor-stack name="mydefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="myinterceptor1">
<param name="excludeMethods">login</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!--默认拦截器栈 -->
<default-interceptor-ref name="mydefaultStack"></default-interceptor-ref>
</package>
<!--注意:现在继承mydefault -->
<package name="p1" extends="mydefault" >
<action name="findAll" class="com.yu.web.action.HelloAction" method="findAll">
<result>/findall.jsp</result>
<result name="register">/register.jsp</result>
</action>
<action name="hello" class="com.yu.web.action.HelloAction" method="saveUser">
<result>/success.jsp</result><!-- 当注册成功之后重定向的结果视图 -->
<result name="exists">/message.jsp</result><!-- 当用户名已经存在之后,转向的结果视图 -->
<result name="input">/register.jsp</result>
</action>
</package>
</struts>
e.上述解决就已经完善了嘛,当然不是
我们在登录之前,拦截器怎么知道,我们的动作方法叫什么了,解决办法是,在动作方法中,注入其参数
<action name="login" class="com.yu.web.action.HelloAction" method="login">
<param name="myinterceptor1.excludeMethods">login</param>
<result>/success.jsp</result>
</action>