Struts2总结之第五章拦截器
拦截器概述:
- 拦截器的概念是在Struts2里面有的。在其它地方没有。
- Struts2是框架,封装了很多的功能,struts2里面封装的功能都是在拦截器里面。
- Struts2里面封装了很多的功能,有很多拦截器,不是每次这些拦截器都执行,每次执行默认的拦截
Struts2里面默认的拦截器位置:
-
struts2-core-2.xxxx.jar—->struts-default.xml,在这里面配置了很多的拦截器,但是只执行默认配置的那些拦截器。
-
struts2中在struts-default.xml文件中声明了所有的拦截器。
而struts2框架默认使用的是defaultStack这个拦截器栈。
在这个拦截器栈中使用了18个拦截器。简单说,struts2框架
在默认情况下,加载了18个拦截器。 -
总的拦截器
<interceptors> <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/> <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/> <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/> <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/> <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" /> <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" /> <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/> <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/> <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/> <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/> <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/> <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/> <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/> <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/> <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/> <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/> <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/> <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/> <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/> <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/> <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" /> <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" /> <interceptor name="datetime" class="org.apache.struts2.interceptor.DateTextFieldInterceptor" /> <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" /> <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" /> <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" /> <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" /> <interceptor name="deprecation" class="org.apache.struts2.interceptor.DeprecationInterceptor" />
-
默认 的拦截器栈
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="datetime"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> <interceptor-ref name="deprecation"/> </interceptor-stack>
拦截器的执行时间
action代理对象创建之后,action目标处理逻辑方法执行之前!!可以在代码中进行调试断点测试(测试时是通过debug调试项目)!!!
通过调试你就可以发现,如果你的拦截器配置的是struts-default或者其它的拦截器组,那么你就会发现,即使你没有在项目中用到某个拦截器,比如我这里并没有实现ModelDriven接口,但是它被运行了。所以,只要你配置了相关的拦截器组,拦截器组里面的所有拦截器不管用或不用都会被执行。
介绍拦截器的底层原理:
struts2拦截器使用的是AOP思想。
AOP的底层实现就是动态代理。
拦截器 采用 责任链 模式
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。
责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行
aop思想(一种开发思想)
- Aop是面向切面(方面) 编程,比如有一个基本功能,想要扩展功能,不通过修改源代码方式扩展功能,经常用的是通过配置文件。(上面的描述还是一个浅显的认识。在讲解Spring时,会更深入了解aop),同时,aop使用了一个技术,那就是动态代理。
责任链模式
-
在java中有很多的设计模式,责任链模式就是其中的一种。
-
责任链模式和过滤链很相似。
-
过滤链:一个请求可有多个过滤器进行过滤,每个过滤器只有做放行才能到下一个过滤器。
在责任链场景:
-
要执行多个操作,有添加,修改,删除三个操作。
-
首先执行添加操作,添加操作执行之后,做类似于放行的操作,执行修改操作,修改操作执行之后做类似于放行操作,执行删除操作。
这里action目标逻辑方法的执行是通过动态代理方式执行,动态代理的方式执行和直接创建action对象执行方法效果是没有区别的。
aop思想和责任链模式如何应用到拦截器里面
-
拦截器在aciton代理对象创建之后,action的目标方法执行之前执行。在拦截器执行之后再执行action方法!!!
-
在action方法执行之前执行默认拦截器,执行过程使用aop思想,在action没有直接调用拦截器的方法,使用配置文件方式进行操作。使用配置文件是关键,把拦截器通过配置引入到项目功能的方式叫做aop思想。
-
在执行拦截器时候,执行很多的拦截器,这个过程使用责任链模式。
-
假如执行三个拦截器,执行拦截器1,执行拦截器1之后做放行操作,执行拦截器2,执行拦截器2之后做放行操作,执行拦截器3,执行拦截器3之后放行,最后执行action目标逻辑方法。
-
在我们的默认拦截器配置里面,那些默认拦截器其实是都会执行的。只是关乎我们到底用不用其具体实现而已,就像是ModelDriven这个类
自定义拦截器
-
Struts2允许我们自定义拦截器,这就使我们能够更加灵活地操作Struts2这个框架了!
-
Struts2提供了Interceptor这个拦截器接口,只要我们实现这个接口,那么这就算是自定义开发拦截器了。
-
大部分时候,我们定义拦截器都是继承AbstractInterceptor这个类…为了学习拦截器的内容,下面就实现Interceptor这个接口了。
编写拦截器类
-
当实现该接口时,有3个需要我们实现的方法:
public class Myinterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { System.out.println("Interceptor init.... "); } @Override public String intercept(ActionInvocation ai) throws Exception { System.out.println("intercept method"); return ai.invoke(); } }
-
init()和destory()都是和拦截器执行顺序有关的方法,
@Override public String intercept(ActionInvocation ai) throws Exception { System.out.println("intercept method"); //调用invoke()方法,代表着放行执行下一个拦截器,如果没有拦截器了,那么就执行Action的业务代码 return ai.invoke(); }
在struts.xml中配置
由于我们配置了自定义拦截器,那么struts默认的拦截器栈是不会执行的。如果我们想要使用默认拦截器栈的功能,就必须把它配置在我们自定义的栈中!
<struts>
<package name="p1" extends="struts-default" namespace="/">
<interceptors>
<!-- 自定义拦截器 -->
<interceptor name="my" class="com.syj.action.Myinterceptor" />
<!-- 自定义拦截器栈 -->
<interceptor-stack name="MyStack">
<interceptor-ref name="my"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 声明全局result,当action的返回结果不存在时会查找 -->
<global-results>
<result name="input">/input.jsp</result>
<result name="login">/input.jsp</result>
</global-results>
<action name="demo1" class="com.syj.action.Demo1">
<!-- 引用自定义的拦截器栈 -->
<interceptor-ref name="MyStack" />
<result>/success.jsp</result>
</action>
</package>
</struts>
拦截器的执行顺序
- Action
public class Demo1 extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("Demo1 Action...");
return SUCCESS;
}
}
执行结果:
从效果图我们可以看出,他们的执行顺序是这样的:
- 当服务器开启的时候,会执行拦截器的init()方法
- 当访问Action时,Action实例被创建
- 创建完Action实例,会调用拦截器的interceptor()方法
- 最后,执行Action的execute()方法
其实很好理解,之前我们使用Struts为我们提供数据自动封装功能的时候,是这样子的:
- 服务器启动,加载配置文件的信息
- 初始化默认的拦截器栈
- 当用户访问Action时,创建Action的实例。拿到Action具体的信息【成员变量、setter和getter】
- 执行拦截器具体的内容,根据Action具体的信息,把web端的数据封装到Action上
- 最后在execute()就可以得到封装后的数据了!
拦截器应用案例
权限控制:
1.login.jsp------>LoginAction------------->book.jsp
登录成功,将用户存储到session。
2.在book.jsp中提供crud链接。
每一个连接访问一个BookAction中一个方法。
要求:对于BookAction中的add,update,delete方法要求用户必须登录后才可以访问。search无要求。
怎样解决只控制action中某些方法的拦截?
1.创建类不在实现Interceptor接口,而是继承其下的一个子类.MethodFilterInterceptor
不用在重写intercept方法,而是重写 doIntercept方法。
2.在struts.xml文件中声明
<interceptors>
<intercept name="" class="">
<param name="includeMethods">add,update,delete</param>
<param name="excludeMethods">search</param>
</intercept>
</interceptors>
-
编写登录的页面。
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <!-- 打印返回的错误结果 --> <s:fielderror/> <s:actionerror/> <form action="${pageContext.request.contextPath}/login" method="post"> 登录:<input type="text" name="username"><br> 密码: <input type="password" name="password"><br> <input type="submit" value="登陆"><br> </form> </body> </html>
2.接下来进行登录的验证,如果账户和密码正确,测跳转到book.jsp
LoginAction.java
public class LoginAction extends ActionSupport implements ModelDriven<User> {
User user = new User();
@Override
public User getModel() {
return user;
}
public String login() throws Exception {
// 在这里就不进行数据库的查询操作
if ("tom".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
ServletActionContext.getRequest().getSession().setAttribute("user", user);
return SUCCESS;
} else {
this.addActionError("用户名或密码错误");
return INPUT;
}
}
}
实体类User.java
package com.syj.entity;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
}
}
3.假如登录成功,跳转到action之后,我们进行增删改查操作(模仿)
book.jsp
<body>
<a href="${pageContext.request.contextPath}/book_add">book add</a><br>
<a href="${pageContext.request.contextPath}/book_update">book update</a><br>
<a href="${pageContext.request.contextPath}/book_delete">book delete</a><br>
<a href="${pageContext.request.contextPath}/book_search">book search</a><br>
</body>
</html>
4.配置struts.xml文件
struts.xml
<struts>
<package name="p1" extends="struts-default" namespace="/">
<global-results>
<result name="input" >/login.jsp</result>
<result name="login" >/login.jsp</result>
</global-results>
<!-- 登录action -->
<action name="login" class="com.syj.action.LoginAction" method="login" >
<result>/book.jsp</result>
</action>
<!-- 权限验证Action -->
<action name="book_*" class="com.syj.action.BookAction" method="{1}" >
</action>
</package>
</struts>
5.此时我们还没有真正实现权限的控制,为此我们需要自定义拦截器,实现只有登陆了才可以进行增删改操作(重点)
自定义拦截器:
public class LoginInteceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation ai) throws Exception {
// 在登录时已经将用户存储到session中
User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user");
// 说明权限不足不允许登录
if (user == null) {
// 存储错误信息并在登录页面打印
BookAction bookAction = (BookAction) ai.getAction();
bookAction.addActionError("权限不足请先登录");
return Action.LOGIN;
}
return ai.invoke();
}
}
注册拦截器
修改struts.xml
<struts>
<package name="p1" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="my" class="com.syj.Interceptor.LoginInteceptor">
<param name="excludeMethods">search</param>
</interceptor>
<interceptor-stack name="MyStack">
<interceptor-ref name="my"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<global-results>
<result name="input" >/login.jsp</result>
<result name="login" >/login.jsp</result>
</global-results>
<!-- 登录action -->
<action name="login" class="com.syj.action.LoginAction" method="login" >
<result>/book.jsp</result>
</action>
<!-- 权限验证Action -->
<action name="book_*" class="com.syj.action.BookAction" method="{1}" >
<interceptor-ref name="MyStack"/>
</action>
</package>
</struts>
6.执行结果
不登录直接访问book.jsp只有book search可以访问
当我们登录之后增删改查都可以就行操作: