一.Struts2有一核心技术是拦截器。英文名为Interceptor。拦截器本来是WebWork框架中一个非常好的支持国际化、校验、类型转换的工具。
如今WebWork和Struts合并成Struts2之后。理所当然也成为Struts2的一部分。
1.拦截器本身也是一个普通的Java对象,它的功能是动态拦截Action调用,在Action运行前后运行拦截器本身提供的各种各样的Web项目需求。当然也能够阻止Action的运行,同一时候也能够提取Action中能够复用的部分。
2.在Struts2中还有个拦截器栈的概念。事实上它就是拦截器的一个集合。它把多个拦截器集合起来,依照在栈中配置的顺序运行,特别是针对Action能够拦截对应的方法或者字段。
二.拦截器在Struts2中的缺省应用与配置:
1.在Web项目中,客户先在视图界面提交一个HTTP请求。在Struts2的ServletDispatcher接收请求,Struts2会查找struts.xml配置文件。依据struts.xml配置文件里定义的拦截器配置,会去调用拦截器。
假设配置了拦截器栈,则依据拦截器在拦截器栈中的前后顺序,一一进行调用。而Struts2自带的源码中也提供了缺省的拦截器配置。
(1).在我们下载的Struts2的包里中。解压后struts-2.3.20目录底下,在src目录中包括了Struts2的全部底层实现源码,我们可到自己安装的Struts2的文件路径下找到srccoresrcmain esources中,当中有个名为struts-default.xml文件。它是Struts2自己定义的配置文件,当中有关拦截器的配置代码便是拦截器在Struts2中的缺省应用。
假设要在MyEclipse工具中查看,能够打开Struts2项目中的Web App Libraries下的 struts2-core-2.3.20.jar下的struts-default.xml文件。
(2).接下来附上struts-default.xml文件里有关拦截器的配置代码。例如以下:
<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" /> <!-- 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"> <param name="excludeParams">^action:.*,^method:.*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="deprecation"/> </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"> <param name="excludeParams">^action:.*,^method:.*</param> </interceptor-ref> <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"> <param name="excludeParams">^action:.*,^method:.*</param> </interceptor-ref> <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"> <param name="excludeParams">^action:.*,^method:.*</param> </interceptor-ref> <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> <!-- 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> <default-interceptor-ref name="defaultStack"/> <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
(3).接下来来解释上图中的struts-default.xml文件的代码:
— 在xml配置文件里配置拦截器和拦截器栈都是以“<interceptors>”开头。以“</interceptors>”结尾。
— 配置拦截器的格式如上面代码所看到的以“<interceptor/>”格式显示。当中有两个属性:name是拦截器的名字,还有一个class是相应的类路径。由于前面说过拦截器也是一个普通的Java对象。
— 拦截器栈的格式是以“<interceptor-stack>”开头,以“</interceptor-stack>”结尾。当中属性name是拦截器栈的名字。在“<interceptor-stack>”和“</interceptor-stack>”之间能够设置拦截器。如struts-default.xml文件里的代码所看到的格式为“<interceptor-ref/>”,当中name属性也是拦截器名字。假设系统执行拦截器栈。都是依照拦截器栈中定义的拦截器先后顺序执行拦截器。
注:1.请大家细致查看那个基础栈的配置,即basicStack这个栈。当中配置的拦截器都是在struts-default.xml文件里定义的拦截器。在struts-default.xml文件里的defaultStack栈里的拦截器配置是我们经经常使用的。
注:2.拦截器栈中不单单能够配置拦截器,它甚至还能够配置拦截器栈。
比方在struts-default.xml文件代码中的“validationWorkflowStack”拦截器栈中就配置了“basicStack”拦截器栈。
这种话,配置的子拦截器栈中的拦截器也会被运行。这就类似于父集合和子集合的概念。
(4).针对struts-default.xml文件里各个拦截器配置一一做介绍。由于假设使用Struts2在Web项目开发中,这些拦截器默认缺省的会被运行的。因此,了解一下Struts2底层的拦截器究竟实现什么功能对开发者来说是非常有帮助的。
2.以下对Struts2中底层的拦截器进行介绍,即了解一下对struts-default.xml文件里的拦截器:
1.alias:对于HTTP请求包括的參数设置别名。
2.autowiring:将某些JavaBean实例自己主动绑定到其它Bean相应的属性中。有点类似Spring的自己主动绑定。
3.chain:在Web项目开发中,曾经使用Struts开发时候常常碰到两个Action互相传递參数或属性的情况。该拦截器就是让前一个Action的參数能够在现有Action中使用。
4.conversionError:从ActionContext中将转换类型时候发生的错误加入到Action的值域错误中,在校验时候常常被使用到来显示类型转换错误的信息。
5.cookie:从Struts2.0.7版本号開始,能够把cookie注入Action中可设置的名字或值中。
6.createSession:自己主动创建一个HTTP的Session。尤其是对须要HTTP的Session的拦截器特别实用。如以下要介绍的TokenInterceptor。
7.debugging:用来对在视图间传递的数据进行调试。
8.execAndWait:不显式运行Action。在视图上显示给用户的是一个正在等待的页面,可是Action事实上是在背后正在运行着的。该拦截器尤其是在进度条开发的时候特别实用。
9.exception:将异常和Action返回的result相映射。
10.fileUpload:支持文件上传功能的拦截器。
11.i18n:支持国际化的拦截器。
12.logger:拥有日志功能的拦截器。
13.modelDriven:Action运行该拦截器的时候。能够将getModel方法得到的result值放入值栈中。
14.scopedModelDriven:运行该拦截器时。它能够从一个scope范围检索和存储model值,通过调用setModel方法去设置model值。
15.params:将HTTP请求中包括的參数值设置到Action中。
16.prepare:假如Action继承了Preparable接口。则会调用prepare方法。
17.staticParams:对于在struts.xml文件里Action中设置的參数设置到相应的Action中。
18.scope:在session或者application范围中设置Action的状态。
19.servletConfig:该拦截器提供訪问包括HttpServletResquest和HttpServletResponse对象的Map的方法。
20.timer:输出Action的运行时间。
21.token:避免反复提交的校验拦截器。
22.tokenSession:和token拦截器类似,但它还能存储提交的数据到Session里。
23.validation:执行在action-validation.xml文件里定义的校验规则。当中action-validation.xml是对一个Action类的校验。
24.workflow:在Action中调用validate校验方法。假设Action有错误则返回到input视图。
25.store:运行校验功能时,该拦截器提供存储和检索Action的全部错误和正确信息的功能。
26.checkbox:视图中假设有checkbox存在的情况。该拦截器自己主动将unchecked的checkbox当作一个參数(通常值为false)记录下来。
这样能够用一个隐藏的表单值来记录全部未提交的checkbox,并且缺省unchecked的checkbox值是布尔类型的,假设视图中checkbox的值设置的不是布尔类型,它就会被覆盖成布尔类型的值。
27.profiling:通过參数来激活或不激活分析检測功能,前提是Web项目是在开发人员模式下。(涉及到调试和性能检验时使用)
28.roles:进行权限配置的拦截器,假设登录用户拥有对应权限才去运行某一特定的Action。
在整个运行过程中,不论什么一个拦截器都能够选择直接返回,从而终止余下的拦截器、Action和Result的运行。
比如,当一个未授权的用户訪问受保护的资源时,运行身份验证的拦截器能够直接返回。
package com.action; import com.opensymphony.xwork2.ActionSupport; public class TimerInterceptorAction extends ActionSupport { public String execute() throws Exception{ Thread.sleep(5000);//让线程睡眠5000毫秒,即睡眠5秒 return SUCCESS; } }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant> <constant name="struts.devMode" value="true"></constant> <package name="default" namespace="/" extends="struts-default"> <action name="timeTest" class="com.action.TimerInterceptorAction"> <interceptor-ref name="timer"/> <result>/index.jsp</result> </action> </package> </struts>
这种方法仅仅运行一次。
利用invocation參数,能够获取action运行的状态。在intercept()方法中。假设要继续运行兴许的部分(包含余下的应用于Action的拦截器、Action和Result),能够调用invocation.invoke()。
假设要终止兴许的运行。能够直接返回一个结果码。框架将依据这个结果码来呈现相应的结果视图。
<interceptors> <interceptor name="replace" class="com.gk.interceptor.MyInterceptor"></interceptor> <interceptor name="replace1" class="com.gk.interceptor.MyInterceptor1"></interceptor> </interceptors>
当中这部分是放在struts.xml文件里的<action>元素下的,如以下代码:
<action name="public" class="com.gk.action.PublicAction"> <result name="success">/success.jsp</result> <result name="login">/success.jsp</result> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="replace"></interceptor-ref> <interceptor-ref name="replace1"></interceptor-ref> </action>
4.接下来附上一个样例,新建一个Struts2项目,项目名为InterceptorTest。此项目用来演示拦截用户评论的脏话,现在社会,网民过多。有些没素养的人往往用一些粗俗的话语来发表言语。所以有必要要拦截一些脏话。像英雄联盟这个游戏,就把lol和送,以及fuck给屏蔽掉了,发送过去会把这些字改变为*和#等。项目结构图例如以下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'news.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <center> 发表评论 <s:form action="public" namespace="/" method="post"> <s:textfield name="title" label="标题"/> <s:textarea name="content" cols="30" rows="5" label="内容"/> <s:submit value="评论"></s:submit> </s:form> </center> </body> </html>
package com.gk.action; import com.opensymphony.xwork2.ActionSupport; public class PublicAction extends ActionSupport { private String title; private String content; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String execute(){ return SUCCESS; } }
(3).接着新建两个自己定义的拦截器类。放在com.gk.interceptor包下,分别为MyInterceptor和MyIntercepter1类,都继承了AbstractInterceptor类。代码分别例如以下:
package com.gk.interceptor; import com.gk.action.PublicAction; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { PublicAction action=(PublicAction) invocation.getAction();//取得Action的实例 String content=action.getContent();//获得action中的content内容,即news.jsp文件表单里的输入的内容 //假设输入的内容包括叼字的话 if(content.contains("叼")){ content=content.replaceAll("叼", "*");//把内容里所有有叼字的所有替换为*号 action.setContent(content);//在又一次设置内容 return invocation.invoke();//返回拦截后结果码 }else{ return action.LOGIN;//假设没有叼字。返回结果码 } } }
MyInterceptor1.java文件的代码例如以下:
package com.gk.interceptor; import com.gk.action.PublicAction; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor1 extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { PublicAction action=(PublicAction) invocation.getAction();//取得Action的实例 String title=action.getTitle();//获得action中的title内容,即news.jsp页面的表单里的输入标题 //假设输入的标题有死字的 if(title.contains("死")){ title=title.replaceAll("死", "*");//把死字所有替换为* action.setTitle(title);//再又一次设置标题内容 return invocation.invoke();//返回拦截后结果码 } return action.LOGIN;//假设没有死字。返回结果码 } }
(4).最后,配置struts.xml文件,代码例如以下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant> <constant name="struts.devMode" value="true"></constant> <package name="interceptor" namespace="/" extends="struts-default"> <interceptors> <interceptor name="replace" class="com.gk.interceptor.MyInterceptor"></interceptor> <interceptor name="replace1" class="com.gk.interceptor.MyInterceptor1"></interceptor> </interceptors> <action name="public" class="com.gk.action.PublicAction"> <result name="success">/success.jsp</result> <result name="login">/success.jsp</result> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="replace"></interceptor-ref> <interceptor-ref name="replace1"></interceptor-ref> </action> </package> </struts>注:这里要注意要在action里必须加上 <interceptor-ref name="defaultStack"></interceptor-ref> 这行代码,否则会报错。
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'success.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 标题为:<s:property value="title"/><br/> 评论内容为:<s:property value="content"/> </body> </html>
(6).部署此项目到Tomcatserver上,开启Tomcatserver,效果例如以下: