您的位置:首页 > 编程语言 > Java开发

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

2017-07-27 22:47 656 查看

拦截器: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"/>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息