拦截器:struts的核心
拦截器和过滤器区别
只对action起作用.
Filter:可以对所有的请求进行过滤.
FilterChain:过滤器链 访问一个资源的时候有可能匹配到多个过滤器
在拦截器中有一个拦截器栈(访问一个action的时候匹配到的多个拦截器)
拦截器的方法
Interceptor:生命周期方法(接口)
init()
destroy()
intercept(ActionInvocation invocation):拦截
struts的执行流程
这个土黄的色部分就是过滤器,现在就只有一个过滤器—StrutsPrepareAndExecuteFilter
详细的执行流程,我用markdown画了个简单的流程图
执行流程源码解析
核心过滤过滤器
//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"/>