struts2 之执行原理|源码解析|拦截器|权限案例(04)

来源:互联网 发布:dnf额外暴击伤害算法 编辑:程序博客网 时间:2024/06/05 03:57

拦截器:struts的核心

拦截器和过滤器区别

只对action起作用.
Filter:可以对所有的请求进行过滤.
FilterChain:过滤器链 访问一个资源的时候有可能匹配到多个过滤器
在拦截器中有一个拦截器栈(访问一个action的时候匹配到的多个拦截器)

拦截器的方法

Interceptor:生命周期方法(接口)
init()
destroy()
intercept(ActionInvocation invocation):拦截

struts的执行流程

执行流程
这个土黄的色部分就是过滤器,现在就只有一个过滤器—StrutsPrepareAndExecuteFilter
详细的执行流程,我用markdown画了个简单的流程图

Created with Raphaël 2.1.01.请求2.核心过滤器3.是否是action4.是action继续往核心走5.创建action的代理对象(ActionProxy)6.action执行处理类(ActionInvocation对象:真正干活的对象)7.把所有的拦截器放到一个容器中8.顺序递归执行这一组拦截器9.执行action原来的逻辑10.方法的返回值封装Result对象11.到达跳转的资源(例如jsp)12.把执行权又交给ActionInvocation对象13.倒序的执行这一组拦截器14.核心过滤器放行后的代码15.服务器16.将response对象拆分成响应信息(响应行 响应头 响应体)返回值浏览器17.浏览器解析执行直接放行yesno

执行流程源码解析

核心过滤过滤器

//StrutsPrepareAndExecuteFilter类的doFilter方法核心部分request = prepare.wrapRequest(request);//对request进行加强                ActionMapping mapping = prepare.findActionMapping(request, response, true);//查找Aciton的映射把request放进去,                //得到请求路径,去struts.xml得到一个Aciton的映射                if (mapping == null) {//如果为空,放行                             boolean handled = execute.executeStaticResourceRequest(request, response);                    if (!handled) {                        chain.doFilter(request, response);                    }                } else {//如果不为空执行拦截器                    execute.executeAction(request, response, mapping);//这个就是进入核心的方法,                }

我们下面可以继续看下execute.executeAction方法

public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {    //继续往下层探究        dispatcher.serviceAction(request, response, servletContext, mapping);    }

继续往下层探究dispatcher.serviceAction方法

    //直接看最重要的东西 try {            UtilTimerStack.push(timerKey);            String namespace = mapping.getNamespace();//            String name = mapping.getName();//拿到要执行的名字            String method = mapping.getMethod();//拿到要执行的方法            Configuration config = configurationManager.getConfiguration();            //创建ActionPorxy代理对象            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                    namespace, name, method, extraContext, true, false);            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());            // if the ActionMapping says to go straight to a result, do it!            //判断有没有结果,如果没执行,肯定没有结果            if (mapping.getResult() != null) {                Result result = mapping.getResult();                result.execute(proxy.getInvocation());            } else {                //执行execute方法,我们继续探究这个方法                proxy.execute();            }

StrutsActionProxy的execute方法,我们继续探究这个方法

public String execute() throws Exception {        ActionContext previous = ActionContext.getContext();        ActionContext.setContext(invocation.getInvocationContext());        try {// This is for the new API://            return RequestContextImpl.callInContext(invocation, new Callable<String>() {//                public String call() throws Exception {//                    return invocation.invoke();//                }//            });            //执行invocation的invoke方法,这里就是真正干活的方法(拦截器),我们继续深入这个方法,看看他是怎么工作的            return invocation.invoke();        } finally {            if (cleanupContext)                ActionContext.setContext(previous);        }    }

继续看DefaultActionInvocation的invoke方法

    //迭代执行拦截方法 if (interceptors.hasNext()) {                final InterceptorMapping interceptor = interceptors.next();                String interceptorMsg = "interceptor: " + interceptor.getName();                UtilTimerStack.push(interceptorMsg);                try {                    //执行拦截方法,每个拦截方法最后都return回来,继续迭代执行,到这一步就不继续深入吧,有兴趣的可以自己去看看                                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);                            }                finally {                    UtilTimerStack.pop(interceptorMsg);                }            } else {                //所有的拦截器都执行完了,最后就把执行权给Action,action执行完了就有一个result对象,然后就根据result进行转发或者重定向                 resultCode = invokeActionOnly();            }

为了深入理解拦截器我们就自己定义一个拦截器

自定义拦截器

1.编写一个类

a.实现Interceptor接口或继承AbstractInterceptor类或继承MethodFilterInterceptor(配置不拦截那些方法)
b.重写拦截的方法

2.编写配置文件

方式1:
a.注册拦截器
b.在action中配置拦截器
方式2:
a.注册拦截器栈
b.在action中配置拦截器栈
//拦截action执行前的操作

protected String doIntercept(ActionInvocation invocation) throws Exception {        //拦截action执行前的操作        System.out.println("MyInterceptor1拦截到了请求");         //放行        String res = invocation.invoke();        //拦截action执行后的操作        System.out.println("MyInterceptor--1--拦截到了响应");        return res;    }

同样的 我做了3个这样简单的拦截器
然后继续注册配置拦截器

<!-- 方式1:注册拦截器,在action中使用拦截器 -->        <interceptors>            <!-- 注册拦截器 -->            <interceptor name="MyInterceptor1" class="com.itheima.a_hello.MyInterceptor1"></interceptor>            <interceptor name="MyInterceptor2" class="com.itheima.a_hello.MyInterceptor2"></interceptor>            <interceptor name="MyInterceptor3" class="com.itheima.a_hello.MyInterceptor3"></interceptor>        </interceptors>        <action name="demo" class="com.itheima.a_hello.DemoAction">            <!-- 在action中使用拦截器 -->            <interceptor-ref name="MyInterceptor1"></interceptor-ref>            <interceptor-ref name="MyInterceptor2"></interceptor-ref>            <interceptor-ref name="MyInterceptor3"></interceptor-ref>            <!-- 若在action显式的使用了某个拦截器,默认的拦截器就失效了  -->            <interceptor-ref name="defaultStack"/>        </action>

最后运行结果如下:
MyInterceptor1拦截到了请求
MyInterceptor2拦截到了请求
MyInterceptor3拦截到了请求
Action执行了~~~
MyInterceptor–3–拦截到了响应
MyInterceptor–2–拦截到了响应
MyInterceptor–1–拦截到了响应
先顺序再倒叙

然后我们可以继续做一个案例,很常用的案例

案例-权限拦截

步骤分析:

先做登录案例

1.创建用户表
2.创建用户的持久化类和映射文件
3.创建user的action service dao

1.将login.htm修改为login.jsp
修改表单提交路径:/crm_/user_login.action
给子标签添加name属性

2.在action中编写login方法
调用service 查询用户 参数:user 返回值:existUser
判断existUser是否为空,
若为空,添加提示信息,转发到login.jsp
若不为空,将用户存入session中,重定向到首页
3.在login.jsp上获取错误信息
在top.jsp上展示用户信息

再做拦截案例

1.编写一个拦截器(继承MethodFilterInterceptor 放行login方法)
逻辑:
判断session中有无用户
  若有:放行
  若无:添加提示信息 转发到login
代码:

public class PrivilegeInterceptor extends MethodFilterInterceptor {    @Override    protected String doIntercept(ActionInvocation invocation) throws Exception {        //判断session有无用户        Object obj = ActionContext.getContext().getSession().get("existUser");        if(obj == null){            //没有登录 生成提示信息 转发到login.jsp            ActionSupport action = (ActionSupport) invocation.getAction();            action.addActionMessage("权限不足,请先登录");            return "login";        }        //放行        return invocation.invoke();    }}

2.编写一个父包 注册拦截器栈

3.让我们所有的package继承父包 就可以在自己的action中使用拦截器

配置如下:

<package name="mypackage" extends="struts-default">        <!-- 注册拦截器栈 -->        <interceptors>            <interceptor name="PrivilegeInterceptor" class="com.itheima.web.interceptor.PrivilegeInterceptor"/>            <!-- 定义拦截器栈 -->            <interceptor-stack name="myStack">                <interceptor-ref name="PrivilegeInterceptor">                <!-- 拦截器放行login方法 -->                    <param name="excludeMethods">login</param>                </interceptor-ref>                <!-- struts 默认的拦截器栈  -->                <interceptor-ref name="defaultStack"></interceptor-ref>            </interceptor-stack>        </interceptors>        <!-- 默认拦截器 -->        <default-interceptor-ref name="myStack"/>        <!-- 全局的结果视图 -->        <global-results>            <result name="login">/login.jsp</result>        </global-results>    </package>

注意:

1.登录页面打开的时候应该在最大的窗口打开
在login.jsp上添加以下js代码

if(top.location != self.location){top.location = self.location;}

2.放行login方法
3.在action中添加错误信息
addActionMessage(String msg)
在jsp上展示错误信息

<s:actionMessage/>

4.在拦截器中获取action

invocation.getAction();

5.设置默认的拦截器

<!-- 默认拦截器 --><default-interceptor-ref name="myStack"/>