Struts2之拦截器

时间:2017-1-11 11:12

——拦截器(interceptor)

1、拦截器介绍

    Struts2拦截器使用的是源自Spring AOP的思想。
    AOP的底层实现就是动态代理。

    拦截器采用“责任链”模式:
        *   在责任链模式里,很多对象都由每一个对象对其下家的引用而连接在一起形成一条链。
        *   责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行。

    在Struts中可以定义多个拦截器,将多个拦截器按照特定顺序组成拦截栈(顺序调用栈中的每一个拦截器)。

    Struts2在struts-default.xml文件中声明了所有的拦截器,而Struts2默认使用的是defaultStack这个拦截器栈,在这个拦截器栈中使用了18个拦截器。简单来说Struts2框架在默认情况下加载了18个拦截器(并没有加载全部拦截器)。

2、使用拦截器可以做什么
    可以使用拦截器进行控制Action的访问,例如:权限操作。

3、在Struts2中怎样使用拦截器
    1)创建Interceptor
        创建一个Java类,实现接口:com.opensymphony.xwork2.interceptor.Interceptor,并重写接口中的三个方法:
            *   init():该方法将在拦截器被创建后立即被调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化操作。
            *   intercept():每拦截一个动作请求,该方法都会被调用一次。
                >   使用ActionInvocation的实例调用invoke()方法进行放行操作。
                >   如果不想放行,可以return一个视图。
            *   destory():该方法将在拦截器被销毁之前调用,它在拦截器的生命周期内只被调用一次。

    2)声明Interceptor
        在struts-default.xml文件中声明了拦截器,但是不能在该文件中添加。
        需要在struts.xml文件中添加,因为struts.xml继承了struts-default.xml。
        在<package>标签下添加:
            <package ...>
                <interceptors>
                    <interceptor name="myinterceptor" class="com.wyc.interceptor.MyInterceptor" />
                </interceptors>
            </package> 

    3)引用Interceptor
        在Action下通过<interceptor-ref>来引用拦截器:
            <action name="demo1" class="com.wyc.action.Demo1Action">
                <interceptor-ref name="myinterceptor" />
            </action>

    4)只要显式声明一个拦截器,那么默认的18个拦截器都会失效,可以再手动引入:
        在<action>标签下添加:
            <action ...>
                <interceptor-ref name="defaultStakc" />
            </action>
        因为struts.xml中的package是struts-default.xml的子类,所以可以直接使用父类内容。
        谁先引入谁先调用。

    5)将拦截器写在<action>下,如果拦截器个数过多,会导致阅读不方便,可以引入一个拦截器栈:
        *   在struts.xml下的<package>下的<interceptors>中定义一个拦截器栈:
            <package ...>
                <interceptors>
                    <interceptor-stack name="myStack">
                        <interceptor-ref name="my" />
                        <interceptor-ref name="defaultStack" />
                    </interceptor-stack>
                </interceptors>
            </package> 
        *   在<action>中引入拦截器栈:
            <action>
                <interceptor-ref name="myStack" />
            </action>

        相当于将拦截器打包然后统一传递给<action>。

4、Interceptor接口
    *   Struts2会依次调用程序员为某个Action注册的每一个拦截器的intercept()方法(通过递归实现)。
    *   每次调用intercept()方法时,Struts2会传递一个ActionInvocation接口的实例(在invoke()方法中通过该实例递归调用invoke()方法)。
    *   ActionInvocation:代表一个给定动作的执行状态,拦截器可以从该类的对象中获得与该动作相关联的Action对象和Result对象,在完成拦截器自己的任务之后,拦截器将调用ActionInvocation对象的invoke()方法前进到Action处理流程的下一个环节。
    *   还可以调用ActionInvocation对象的addPreResultListener()方法给ActionInvocation对象注册一个或多个PreResultListener监听器,该监听器对象可以在动作执行完毕后,开始执行动作结果之前做些事情。
    *   AbstractInterceptor类实现了Interceptor接口,并为int()、destory()方法提供了一个空白的实现。

5、Struts2自带拦截器介绍

    图片

    图片

3、分析拦截器原理

    源代码执行流程
        1)Struts2框架开始执行的位置是在xml文件中配置的过滤器,所以在StrutsPrepareAndExecuteFilter中查找:
            在doFilter()方法中有一行代码:ExecuteOperations execute.executeAction(request, response, mapping),执行Action操作。
        2)在executeAction()方法执行过程中会访问Dispatcher类中的serviceAction()方法,在这个方法中会创建一个动态代理对象:
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
            proxy就是Action的代理对象。
        3)然后查看ActionInvocation的代理方法
            发现ActionInvocation是一个接口,可以查看其子类:DefaultActionInvocation。
            在动态代理中,InvocationHandler负责调用方法,而ActionInvocation就相当于继承了InvocationHandler。
            在DefaultActionInvocation中有这么一段代码:

            if (interceptors.hasNext()) {    // 判断是否有下一个拦截器
                    final InterceptorMapping interceptor = interceptors.next();    // 得到拦截器
                    String interceptorMsg = "interceptor: " + interceptor.getName();
                    UtilTimerStack.push(interceptorMsg);
                    try {
                        // 得到拦截器对象然后调用拦截方法,进行拦截操作,并传入自己的对象。 
                        resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                    }
                    finally {
                        UtilTimerStack.pop(interceptorMsg);
                    }
                }
            其中interceptors是一个Iterator<Interceptor Mapping>迭代器,其中保存着所有的拦截器。


            通过源代码分析,发现DefaultActionInvocation中是通过递归完成所有拦截器的调用操作。

        图片

当程序开始执行时,会将所有的拦截器存入一个List,然后在DefaultActionInvocation的invoke()方法中进行迭代,如果集合不为空,则会取出拦截器对象然后调用拦截方法,所以会调用自定义拦截器对象的方法,然后传入当前类的对象。在执行完拦截操作后,会继续由“传入的当前类对象”调用invoke()方法,直至集合为空,这是递归操作。

4、案例——权限控制

通过login.jsp访问LoginAction,登录成功后将用户存储到session中,然后跳转到book.jsp
在book.jsp中提供CRUD链接,每一个链接都可以访问BookAction中的对应方法。

要求:对于BookAction中的add,update,delete方法必须要求用户登录后才能访问,search无要求。

问题:怎样解决只控制Action中某些方法的拦截操作?
    1)创建类,不实现Interceptor接口,而是继承其下的一个子类:MethodFIlterInteceptor
        不需要重写intercept()方法,而是重写doIntercept()方法,因为子类已经重写了intercept()方法,并在该方法中调用了doIntercept()方法。

    2)在struts.xml文件中声明:
        <interceptors>
            <interceptor name="" class="">
                <!-- 可以在这里为MethodFilterInterceptor类的参数进行赋值 -->
                <param name="includeMethods">add, update, delete</param> // 表示对这些方法进行拦截
                <param name="excludeMethods">search</param> // 表示不对该方法进行拦截操作
            </interceptor>
        </interceptors>
    3)MethodFilterInterceptor部分源码
        public abstract class MethodFilterInterceptor extends AbstractInterceptor {

 
            protected Set<String> excludeMethods = Collections.emptySet();    // 用来保存不被执行的方法
            protected Set<String> includeMethods = Collections.emptySet();    // 用来保存要执行的方法
            @Override
            public String intercept(ActionInvocation invocation) throws Exception {
                if (applyInterceptor(invocation)) {
                    return doIntercept(invocation);
                } 
                return invocation.invoke();
            }
            protected abstract String doIntercept(ActionInvocation invocation) throws Exception;
        }



    4)实现:
        JSP页面:
            *   login.jsp
                提供登录表单
            *   book.jsp
                提供CRUD四个超链接,分别访问BookAction的CRUD方法。
            *   error.jsp
                显示错误信息
            *   success.jsp
                显示成功信息

        Action:
            *   LoginAction
                校验用户名密码,登录成功后将用户保存到session中,然后跳转到book.jsp

            *   public class LoginAction extends ActionSupport implements ModelDriven {

 
                    private User user = new User();
 
                    public String login() {
 
                        if (user.getUsername().equals("wyc") && user.getPassword().equals("wyc")) {
                            ActionContext.getContext().getSession().put("user", user);
                            return "book";
                        } else {
                            ActionContext.getContext().put("msg", "用户名或密码错误");
                            return "error";
                        }
                    }
 
                    public Object getModel() {
                        return this.user;
                    }
                }


            *   BookAction
                声明CRUD方法,设置返回视图。

        Interceptor:
            *   BookInterceptor
                继承MethodFilterInterceptor类,重写doIntercept()方法。
            *   public class BookInterceptor extends MethodFilterInterceptor {

                    @Override
                    protected String doIntercept(ActionInvocation ai) throws Exception {
                        User user = (User) ActionContext.getContext().getSession().get("user");
                        if (user != null) {
                            return ai.invoke();
                        } else {
                            ActionContext.getContext().put("msg", "请先登录");
                            return "error";
                        }
                    }
                }


        Domain:
            *   User
                提供用户名、密码。

        struts.xml
            *   配置自定义拦截器,分别设置CRUD需要拦截的方法。

            *   <struts>
                    <package name="default" namespace="/" extends="struts-default">
 
                        <interceptors>
 
                            <interceptor name="bookInterceptor" class="com.wyc.interceptor.BookInterceptor" />

                            <interceptor-stack name="bookStack">
                                <interceptor-ref name="bookInterceptor"></interceptor-ref>
                                <interceptor-ref name="defaultStack"></interceptor-ref>
                            </interceptor-stack>

                        </interceptors>
 
                        <action name="login" class="com.wyc.action.LoginAction" method="login">
                            <result name="error">/error.jsp</result>
                            <result name="book" type="redirect">/book.jsp</result>
                        </action>
 
                        <action name="Book_*" class="com.wyc.action.BookAction" method="{1}">
                            <result name="error">/error.jsp</result>
                            <result name="success">/success.jsp</result>

                            <interceptor-ref name="bookStack">
                                <param name="includeMethods">add,update,delete</param>
                                <param name="excludeMethods">search</param>
                            </interceptor-ref>
 
                        </action>
                    </package>
                </struts>



——总结

    1、拦截器介绍
        拦截器使用了AOP的思想
        采用了责任链模式
        使用的是defaultStack拦截器栈

    2、拦截器的使用
        如何定义拦截器
        如何注册拦截器
        如何指定需要拦截的方法

    3、拦截器的原理
        分析源代码的执行流程

    4、Interceptor与Filter的区别

原文地址:https://www.cnblogs.com/wwwwyc/p/6375425.html