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

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/yjy91913/article/details/76222784

拦截器:struts的核心

拦截器和过滤器区别

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

拦截器的方法

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

struts的执行流程

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

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

执行流程源码解析

核心过滤过滤器

//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"/>

猜你喜欢

转载自blog.csdn.net/yjy91913/article/details/76222784