zoukankan      html  css  js  c++  java
  • 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的区别

  • 相关阅读:
    [转].NET委托:一个C#睡前故事
    有关睡觉的学问
    [转]电话号码规范化规则正则表达式
    验证邮件地址的正则表达式
    初学UML之用例图
    没有不可突破的系统……
    生成树协议Spanning Tree Protocol
    两种图片漂浮的代码
    转:静态路由实际应用
    Cisco 2600 NAT 配置 实例
  • 原文地址:https://www.cnblogs.com/wwwwyc/p/6375425.html
Copyright © 2011-2022 走看看