一、拦截器
- Struts2拦截器是在访问某个Action或Action的某个方法之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现
- AOP:面向切面编程,其实现原理:动态代理模式
- 拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的代码方式
- 拦截器栈(Interceptor Stack):Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截器的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用
- 拦截器在设计和程序结构上的优点:
- 拦截器能把很多功能从Action中独立出来,分散到不同的拦截器里面,减少了Action的代码。拦截器和Action本身的功能都更单一了。当通用的功能代码被封装在拦截器里面(代码模块化),就可以对不同的Action,根据功能需要,来配置相应功能的拦截器了。提高了拦截器所实现的功能的重用性,也变相实现了装配式和可插拔式的体系结构,使得整个系统结构变得更灵活。
二、Struts2执行流程
- 1、客户端发送请求
- 2、该请求经过一系列的过滤器(Filter):其中可选过滤器ActionContextCleanUp,帮助Struts2和其他框架集成。例如:SiteMesh Plugin
- 3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper,来决定该请求是否需要调用某个Action
- 4、若ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy。
- 5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
- 6、ActionProxy创建一个ActionInvocation的实例
- 7、ActionInvocation实例调用Action的前后,涉及到相关拦截器(Intercepter)的调用
- 8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果是一个JSP或其他页面(也可以是其他的Action链)。JSP页面展现可使用Struts2框架中的标签(该过程会涉及ActionMapper)。
三、Struts2内置拦截器
<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="org.apache.struts2.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="paramRemover" class="com.opensymphony.xwork2.interceptor.ParameterRemoverInterceptor"/>
<interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInterceptor"/>
<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="annotationParameterFilter" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationParameterFilterInterceptor" />
<interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />
<interceptor name="noop" class="org.apache.struts2.interceptor.NoOpInterceptor" />
<!-- Empty stack - performs no operations -->
<interceptor-stack name="emptyStack">
<interceptor-ref name="noop"/>
</interceptor-stack>
<!-- Basic stack -->
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
<!-- Sample validation and workflow stack -->
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>
<!-- Sample file upload stack -->
<interceptor-stack name="fileUploadStack">
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- Sample model-driven stack -->
<interceptor-stack name="modelDrivenStack">
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- Sample action chaining stack -->
<interceptor-stack name="chainStack">
<interceptor-ref name="chain"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- Sample i18n stack -->
<interceptor-stack name="i18nStack">
<interceptor-ref name="i18n"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- An example of the paramsPrepareParams trick. This stack
is exactly the same as the defaultStack, except that it
includes one extra interceptor before the prepare interceptor:
the params interceptor.
This is useful for when you wish to apply parameters directly
to an object that you wish to load externally (such as a DAO
or database or service layer), but can't load that object
until at least the ID parameter has been loaded. By loading
the parameters twice, you can retrieve the object in the
prepare() method, allowing the second params interceptor to
apply the values on the object. -->
<interceptor-stack name="paramsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="params"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<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-stack>
<!-- A complete stack with all the common interceptors in place.
Generally, this stack should be the one you use, though it
may do more than you need. Also, the ordering can be
switched around (ex: if you wish to have your servlet-related
objects applied before prepare() is called, you'd need to move
servletConfig interceptor up.
This stack also excludes from the normal validation and workflow
the method names input, back, and cancel. These typically are
associated with requests that should not be validated.
-->
<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-stack>
<!-- The completeStack is here for backwards compatibility for
applications that still refer to the defaultStack by the
old name -->
<interceptor-stack name="completeStack">
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
<!-- Sample execute and wait stack.
Note: execAndWait should always be the *last* interceptor. -->
<interceptor-stack name="executeAndWaitStack">
<interceptor-ref name="execAndWait">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="execAndWait">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
1、常见拦截器
1:params拦截器
这个拦截器偷偷的把请求参数设置到相应的Action的属性去的,并自动进行类型转换。
2.modelDriven拦截器
如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中。
3.execption拦截器
顾名思义,在抛出异常的时候,这个拦截器起作用。最好把它放在第一位,让它能捕获所有的异常。
4.validation拦截器
调用验证框架读取 *-validation.xml文件,并且应用在这些文件中声明的校验。
5.token拦截器
核对当前Action请求(request)的有效标识,防止重复提交Action请求。
6.fileUpload拦截器
用来处理文件上传
7.workflow拦截器
调用Action的validate方法,一旦有错误返回,重新定位到INPUT结果视图
8.servletConfig
通过感知接口,获取感应对象
9.store:保证在两次请求之间共享数据的.
2、servletConfig拦截器原理
- 实现ServletRequestAware接口
package com.revanwang.servlet;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//1、通过让Action类去实现感知接口.
public class ActionServlet1 extends ActionSupport implements
ServletRequestAware, //获取 HttpServletRequest 对象
ServletResponseAware
{
private HttpServletRequest request;
private HttpServletResponse response;
@Override
public void setServletRequest(HttpServletRequest httpServletRequest) {
this.request = httpServletRequest;
}
@Override
public void setServletResponse(HttpServletResponse httpServletResponse) {
this.response = httpServletResponse;
}
@Override
public String execute() throws Exception {
System.out.println(this.request.getParameter("name")+"--"+this.response);
return NONE;
}
}
- 从struts2-core-2.5.20.jar中struts-default.xml中
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
- ServletConfigInterceptor源码
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
private static final long serialVersionUID = 605261777858676638L;
public ServletConfigInterceptor() {
}
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
ActionContext context = invocation.getInvocationContext();
HttpServletRequest request;
if (action instanceof ServletRequestAware) {
request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
((ServletRequestAware)action).setServletRequest(request);
}
if (action instanceof ServletResponseAware) {
HttpServletResponse response = (HttpServletResponse)context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
((ServletResponseAware)action).setServletResponse(response);
}
if (action instanceof ParameterAware) {
context.getParameters().applyParameters((ParameterAware)action);
}
if (action instanceof HttpParametersAware) {
((HttpParametersAware)action).setParameters(context.getParameters());
}
if (action instanceof ApplicationAware) {
((ApplicationAware)action).setApplication(context.getApplication());
}
if (action instanceof SessionAware) {
((SessionAware)action).setSession(context.getSession());
}
if (action instanceof RequestAware) {
((RequestAware)action).setRequest((Map)context.get("request"));
}
if (action instanceof PrincipalAware) {
request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
if (request != null) {
((PrincipalAware)action).setPrincipalProxy(new ServletPrincipalProxy(request));
}
}
if (action instanceof ServletContextAware) {
ServletContext servletContext = (ServletContext)context.get("com.opensymphony.xwork2.dispatcher.ServletContext");
((ServletContextAware)action).setServletContext(servletContext);
}
return invocation.invoke();
}
}
- 执行过程
Object action = invocation.getAction();
ActionContext context = invocation.getInvocationContext();
HttpServletRequest request;
if (action instanceof ServletRequestAware) {
request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
((ServletRequestAware)action).setServletRequest(request);
}
1:通过invocation.getAction()来获取自定义Action类,然后在判断该Action类是否实现ServletRequestAware接口,如果实现ServletRequestAware接口,就会调用自定义Action类中的setServletRequest方法
四、在Struts2中自定义拦截器
通过(登录检查拦截器,访问某一个页面时必须要先登录,登录之后才能访问)例子来实现自定义拦截器
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册界面</title>
</head>
<body>
<form action="/interceptorLogin" method="post">
用户名:<input type="text" name="user.userName">
<br>
密码 :<input type="text" name="user.passWord">
<br>
<input type="submit" value=" 注 册 ">
</form>
</body>
</html>
- welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
欢迎${sessionScope.user.userName}
</body>
</html>
- User
package com.revanwang.interceptor;
import lombok.Data;
@Data
public class User {
private String userName;
private String passWord;
}
- Action类
package com.revanwang.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import lombok.Getter;
import lombok.Setter;
public class LoginAction extends ActionSupport {
@Setter
@Getter
private User user; //获取请求参数
@Override
public String execute() throws Exception {
System.out.println("Login....." + this.user);
//保存user数据到 session 中
ActionContext.getContext().getSession().put("user", this.user);
return SUCCESS;
}
}
- struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="default" extends="struts-default" namespace="/">
<action name="interceptorLogin" class="com.revanwang.interceptor.LoginAction">
<result>/login/welcome.jsp</result>
</action>
</package>
</struts>
-
上面的缺点就是无论是否登陆成功都可以访问到welcome.jsp,这显然不符合需求。为了只能在登录成功后才能访问welcome.jsp.
- 1、直接判断Action中的请求参数,但是这种方法存在硬编码
- 2、自定义拦截器(checkLoginInterceptor)
CheckLoginInterceptor
package com.revanwang.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import java.util.Map;
public class CheckLoginInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Map<String, Object> sessionMap = invocation.getInvocationContext().getSession();
User user = (User) sessionMap.get("user");
System.out.println("CheckLoginInterceptor. . . ." + sessionMap + "\n" + user);
if (user == null || user.getUserName().equals("".trim())) {
System.out.println("CheckLoginInterceptor. . . .empy");
return "login";
}
return invocation.invoke();
}
}
- struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="default" extends="struts-default" namespace="/">
<!--1、注册拦截器-->
<interceptors>
<!-- 自定义检测登录拦截器 -->
<interceptor name="checkLogin" class="com.revanwang.interceptor.CheckLoginInterceptor"></interceptor>
<!-- 自定义拦截器栈
包含 拦截器 / 拦截器栈
-->
<interceptor-stack name="myStack">
<interceptor-ref name="checkLogin"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 2、设置当前package的默认引用拦截器 -->
<default-interceptor-ref name="myStack"/>
<!-- 3、全局的结果视图 -->
<global-results>
<result name="login" type="redirect">
/login/login.jsp
</result>
</global-results>
<!-- 4、Action -->
<!-- LoginAction
redirectAction: 跳转 checkAction Action
-->
<action name="interceptorLogin" class="com.revanwang.interceptor.LoginAction">
<!-- 引用默认的拦截器
对于LoginAction,不需要做登录检查,但是需要获取请求参数
-->
<interceptor-ref name="defaultStack"/>
<result type="redirectAction">checkAction</result>
</action>
<!--
自定义了一个添加 拦截器 的action
该 checkAction Action默认继承com.opensymphony.xwork2.ActionSupport
默认方法 excetue
默认回调 SUCCESS
-->
<action name="checkAction">
<result>/login/welcome.jsp</result>
</action>
</package>
</struts>
3、拦截器设置参数
- CheckLoginInterceptor
package com.revanwang.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import java.util.Arrays;
import java.util.Map;
public class CheckLoginInterceptor extends AbstractInterceptor {
//需要放行的action名称
private String[] unCheckedActionNames = {};
//通过拦截器设置对应参数
public void setActionNames(String actionNames) {
if (actionNames != null) {
this.unCheckedActionNames = actionNames.split(",");
}
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//拦截器放行
String actionName = invocation.getProxy().getActionName();
System.out.println("unCheckedActionNames:===" + actionName);
if (Arrays.asList(this.unCheckedActionNames).contains(actionName)) {
System.out.println("unCheckedActionNames:===" + actionName);
return invocation.invoke();
}
Map<String, Object> sessionMap = invocation.getInvocationContext().getSession();
User user = (User) sessionMap.get("user");
if (user == null || user.getUserName().equals("".trim())) {
System.out.println("CheckLoginInterceptor. . . .empy");
return "login";
}
return invocation.invoke();
}
}
- struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="default" extends="struts-default" namespace="/">
<!--1、注册拦截器-->
<interceptors>
<!-- 自定义检测登录拦截器 -->
<interceptor name="checkLogin" class="com.revanwang.interceptor.CheckLoginInterceptor">
<!--放行拦截器的名称-->
<param name="actionNames">lt,dl</param>
</interceptor>
<!-- 自定义拦截器栈
包含 拦截器 / 拦截器栈
-->
<interceptor-stack name="myStack">
<interceptor-ref name="checkLogin"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 2、设置当前package的默认引用拦截器 -->
<default-interceptor-ref name="myStack"/>
<!-- 3、全局的结果视图 -->
<global-results>
<result name="login" type="redirect">
/login/login.jsp
</result>
</global-results>
<!-- 4、Action -->
<!-- LoginAction
redirectAction: 跳转 checkAction Action
-->
<action name="interceptorLogin" class="com.revanwang.interceptor.LoginAction">
<!-- 引用默认的拦截器
对于LoginAction,不需要做登录检查,但是需要获取请求参数
-->
<interceptor-ref name="defaultStack"/>
<result type="redirectAction">checkAction</result>
</action>
<action name="lt" class="com.revanwang.interceptor.LoginAction" method="loginList">
<!-- <result type="redirectAction">checkAction</result>-->
<interceptor-ref name="defaultStack"/>
<result name="list" type="dispatcher">/WEB-INF/JSP/list.jsp</result>
</action>
<action name="dl" class="com.revanwang.interceptor.LoginAction" method="loginDelete">
<result name="delete" type="dispatcher">/login/welcome.jsp</result>
</action>
<action name="sv" class="com.revanwang.interceptor.LoginAction" method="loginSave">
<result name="save" type="redirect">
<param name="location">/login/welcome.jsp</param>
</result>
</action>
<!--
自定义了一个添加 拦截器 的action
该 checkAction Action默认继承com.opensymphony.xwork2.ActionSupport
默认方法 excetue
默认回调 SUCCESS
-->
<action name="checkAction">
<result>/login/welcome.jsp</result>
</action>
</package>
</struts>