拦截器
概述
Interceptor (拦截器):起到拦截客户端对 Action 请求的作用。
- Filter:过滤器,过滤客户端向服务器发送的请求。
- Interceptor:拦截器,拦截的是客户端对 Action 的访问,是更细粒度化的拦截。
Struts2 框架的核心功能都是通过拦截器实现。
自定义拦截器
编写拦截器类
编写一个类实现 Interceptor 接口或继承 AbstractInterceptor 类。
package com.zze.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class Test1Interceptor implements Interceptor { @Override public void destroy() { System.out.println("from Test1Interceptor.destroy"); } @Override public void init() { System.out.println("from Test1Interceptor.init"); } @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("from Test1Interceptor.intercept"); return invocation.invoke(); } }
com.zze.interceptor.Test1Interceptor 方式一:实现 Interceptor 接口
package com.zze.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class Test2Interceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("Test2Interceptor.intercept"); return invocation.invoke(); } }
com.zze.interceptor.Test2Interceptor 方式二:继承 AbstractInterceptor 类
配置拦截器
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true"/> <package name="test" extends="struts-default" namespace="/"> <!--定义拦截器--> <interceptors> <interceptor name="interceptor1" class="com.zze.interceptor.Test1Interceptor"/> <interceptor name="interceptor2" class="com.zze.interceptor.Test2Interceptor"/> </interceptors> <action name="*" class="com.zze.action.{1}Action"> <result>/index.jsp</result> <!--引入拦截器--> <interceptor-ref name="defaultStack"/> <interceptor-ref name="interceptor1"/> <interceptor-ref name="interceptor2"/> </action> </package> </struts>
struts.xml 方式一:引入拦截器
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true"/> <package name="test" extends="struts-default" namespace="/"> <!--定义拦截器--> <interceptors> <interceptor name="interceptor1" class="com.zze.interceptor.Test1Interceptor"/> <interceptor name="interceptor2" class="com.zze.interceptor.Test2Interceptor"/> <!--定义拦截器栈--> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="interceptor1"/> <interceptor-ref name="interceptor2"/> </interceptor-stack> </interceptors> <action name="*" class="com.zze.action.{1}Action"> <result>/index.jsp</result> <!--引入拦截器栈--> <interceptor-ref name="myStack"/> </action> </package> </struts>
struts.xml 方式二:引入拦截器栈
补充
Struts2 还为我们提供了一些功能增强的过滤器,我们只需要继承它简单配置即可,例如:
package com.zze.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; /** * MethodFilterInterceptor 可以让我们很简单的控制要拦截的方法和不需拦截的方法 */ public class TestInterceptor extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("from TestInterceptor.doIntercept"); return invocation.invoke(); } }
com.zze.interceptor.TestInterceptor
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true"/> <package name="test" extends="struts-default" namespace="/"> <!--定义拦截器--> <interceptors> <interceptor name="methodInterceptor" class="com.zze.interceptor.TestInterceptor"/> </interceptors> <action name="*_*" class="com.zze.action.{1}Action" method="{2}"> <result>/index.jsp</result> <interceptor-ref name="methodInterceptor"> <!--配置不拦截的方法名--> <param name="excludeMethods">login,index</param> <!--配置要拦截的方法名--> <param name="includeMethods">home,list</param> </interceptor-ref> </action> </package> </struts>
struts.xml
Struts2执行流程
官方架构图
源码分析
依旧是从核心过滤器的 doFilter 方法开始:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { // 判断当前请求 URL 是否在不处理范围内 if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { // 设置编码,默认 request.setCharacterEncoding("UTF-8") prepare.setEncodingAndLocale(request, response); // 创建 Action 及创建 ValueStack 值栈 prepare.createActionContext(request, response); // 将本次请求相关配置绑定到当前线程 ThreadLocal prepare.assignDispatcherToThread(); // 包装原生 request ,对其进行增强 request = prepare.wrapRequest(request); // 找到此次请求对应配置文件 struts.xml 中的映射相关信息,封装到 ActionMapping 实例 ActionMapping mapping = prepare.findActionMapping(request, response, true); if (mapping == null) { // 未找到映射信息 // 查看此次请求目标是否是静态资源 boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else {// 找到了映射信息 // 执行拦截器及 Action execute.executeAction(request, response, mapping); } } } finally { // 清理请求信息 prepare.cleanupRequest(request); } }
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter#doFilter
进到 29 行的 execute.executeAction 方法:
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { dispatcher.serviceAction(request, response, mapping); }
org.apache.struts2.dispatcher.ng.ExecuteOperations#executeAction
继续进到 dispatcher.serviceAction 方法:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { Map<String, Object> extraContext = createContextMap(request, response, mapping); // 从 request 中获取值栈 ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); boolean nullStack = stack == null; if (nullStack) { // 如果从 request 中未获取到值栈,则从 ActionContext 中取出值栈赋值给 stack ActionContext ctx = ActionContext.getContext(); if (ctx != null) { stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); // 创建 Action 代理对象 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false); // 将值栈放入 request request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { // Action 代理开始执行过滤器和 Action proxy.execute(); } if (!nullStack) { // 将已存在的值栈放入 Request request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { logConfigurationException(request, e); sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { if (handleException || devMode) { sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } else { throw new ServletException(e); } } finally { UtilTimerStack.pop(timerKey); } }
org.apache.struts2.dispatcher.Dispatcher#serviceAction
Action 及过滤器的执行在 34 行,查看 proxy.execute 方法:
public String execute() throws Exception { ActionContext previous = ActionContext.getContext(); ActionContext.setContext(invocation.getInvocationContext()); try { return invocation.invoke(); } finally { if (cleanupContext) ActionContext.setContext(previous); } }
org.apache.struts2.impl.StrutsActionProxy#execute
在这里又执行 invocation.invoke 方法:
public String invoke() throws Exception { String profileKey = "invoke: "; try { UtilTimerStack.push(profileKey); if (executed) { throw new IllegalStateException("Action has already executed"); } // interceptors 是一个 Iterator (迭代器)对象,存放了所有拦截器的引用 if (interceptors.hasNext()) { // 如果存在下一个未迭代的拦截器 final InterceptorMapping interceptor = interceptors.next(); // 获取到拦截器 String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { // 执行拦截器的 intercept 方法 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally { UtilTimerStack.pop(interceptorMsg); } } else { // 如果不存在下一个未迭代的拦截器 // 开始执行 Action resultCode = invokeActionOnly(); } if (!executed) { if (preResultListeners != null) { LOG.trace("Executing PreResultListeners for result [#0]", result); for (Object preResultListener : preResultListeners) { PreResultListener listener = (PreResultListener) preResultListener; String _profileKey = "preResultListener: "; try { UtilTimerStack.push(_profileKey); listener.beforeResult(this, resultCode); } finally { UtilTimerStack.pop(_profileKey); } } } if (proxy.getExecuteResult()) { executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }
com.opensymphony.xwork2.DefaultActionInvocation#invoke
重点就在这个方法的 10-24 行了,这里在迭代所有拦截器,并且在 16 行把当前 DefaultActionInvocation 实例作为 invocation 参数传入执行了当前迭代的拦截器的 intercept 方法。而我们已经知道,拦截器中放行就是通过调用传入的 invocation 参数的 invocation.invoke 方法,即当前 invoke 方法。没错,这是一个递归!!!
Struts2 就是通过递归来迭代调用拦截器,这个递归能维持下去的条件有两个:
1、迭代器 interceptors 中还存在未迭代的拦截器。
2、在迭代器的 intercept 方法中必须调用 invocation.invoke 方法。
总结上述,Struts2 的执行流程如下:
客户端向服务器发送一个 Action 请求,首先执行核心过滤器 (StrutsPrepareAndExecuteFilter) 的 doFilter 方法。
在这个方法中,调用了 ExecuteOperations 实例 execute 的 executeAction 方法,而 executeAction 方法中又执行了 Dispatcher 实例 dispatcher 的 serviceAction 方法。
在 serviceAction 中创建了 Action 代理对象 proxy,这个代理对象为 StrutsActionProxy 的实例,接着执行了 Action 代理对象的 execute 方法。
在 execute 方法中又执行了 DefaultActionInvocation 的实例 invocation 的 invoke 方法。
在 invoke 方法中递归迭代执行拦截器,当拦截器迭代完毕,就会执行目标 Action 的目标方法,最后 Struts2 处理 Action 返回的逻辑视图结果,将处理结果交给 response 对象响应给浏览器。
通过上述代码也可以看到,Action 的执行时机是在迭代器正常执行完之后,到这里可以得出结论:
如果在迭代器中未调用 invocation.invoke ,则后续的迭代器不会被执行,且 Action 也不会被执行,这就是 invocation.invoke 放行的原理。
标签库
通用标签库
判断
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>标签库测试</title> </head> <body> <s:set var="i" value="3" scope="request"/> <s:if test="#request.i>3"> i>3 </s:if> <s:elseif test="#request.i<3"> i<3 </s:elseif> <s:else> i=3 </s:else> </body> </html>
例:if/elseif/else
循环
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>标签库测试</title> </head> <body> <%--当前遍历对象会被放到值栈的root和context中--%> <%--遍历 List--%> <s:iterator var="letter" value="{'a','b','c'}"> <s:property value="letter"></s:property> </s:iterator> <hr> <%--遍历 Map--%> <s:iterator value="#{'a':'1','b':'2','c':'3'}"> key: <s:property value="key"/> value: <s:property value="value"/> <br> </s:iterator> <hr> <%--类似 for 循环--%> <s:iterator var="i" begin="0" end="10" step="2" status="s"> <s:property value="#s.count"/> : <s:property value="i"/> <br> </s:iterator> </body> </html>
例:iterator
日期
<%@ page import="java.util.Date" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>标签库测试</title> </head> <body> <% Date date = new Date(); request.setAttribute("date", date); %> <s:date name="#request.date" /> <br> <s:date name="#request.date" format="yyyy年MM月dd日 HH:mm:ss" /> </body> </html>
例:date
UI标签库
<%@ page import="java.util.Date" %> <%@ page import="java.util.TreeMap" %> <%@ page import="java.util.HashMap" %> <%@ page import="java.util.Map" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>标签库测试</title> </head> <body> <%-- Struts2 默认会表单中标签套上 table 如果不想要 Struts2 提供的样式,可更改默认常量 struts.ui.theme struts.ui.theme 有三个可选值: xhtml : 默认值。 simple : 无样式提供。 ajax : 这个主题里的模板以 xhtml 主题里的模板为基础,但增加了一些ajax功能。 除了更改配置文件中的常量,还可以通过修改 s:form 上的 theme 属性来让主题只对当前表单生效 --%> <% Map<String, String> gender = new HashMap<String, String>(); gender.put("1", "男"); gender.put("2", "女"); request.setAttribute("gender", gender); Map<String, String> address = new HashMap<String, String>(); address.put("hk", "香港"); address.put("jp", "日本"); request.setAttribute("address", address); Map<String, String> hobby = new HashMap<String, String>(); hobby.put("1", "吃饭"); hobby.put("2", "睡觉"); hobby.put("3", "打豆豆"); request.setAttribute("hobby",hobby); %> <s:form namespace="/" action="Test1"> <%--隐藏域--%> <s:hidden name="id"/> <%--文本框--%> <s:textfield name="username" label="用户名"/> <%--密码框--%> <s:password name="password" label="密码"/> <%--单选框--%> <s:radio list="#request.gender" name="gender" label="性别"/> <%--下拉框--%> <s:select list="#request.address" name="address" label="地点"/> <%--多选框--%> <s:checkboxlist list="#request.hobby" name="hobby" label="爱好"/> <%--文本域--%> <s:textarea rows="3" cols="10" value="默认值" label="简介" /> <%--提交按钮--%> <s:submit value="提交"/> </s:form> </body> </html>
例:
补充
数据校验
手动编码方式
package com.zze.action; import com.opensymphony.xwork2.ActionSupport; public class Test1Action extends ActionSupport { private String name; public void setName(String name) { this.name = name; } @Override public String execute() throws Exception { System.out.println("................."); return super.execute(); } /** * 编写一个类,继承 ActionSupport,重写 validate 方法 * 每次请求此 Action 都会先执行 validate 方法 * 如果验证有错误,将错误通过 this.addFieldError 或 this.addActionError 交给 Struts2 * 然后 Struts2 会返回 input 逻辑视图,手动定义好 input 跳转到的页面 * 在页面可以通过 <s:actionerror/> <s:fielderror/> 标签获取到错误信息 */ @Override public void validate() { if(name == null || name.trim().length() == 0){ this.addFieldError("name","用户名不能为空"); } } }
例 1:对整个 Action 进行校验
package com.zze.action; import com.opensymphony.xwork2.ActionSupport; public class Test2Action extends ActionSupport { private String name; public void setName(String name) { this.name = name; } public String add() { System.out.println("from add...."); return SUCCESS; } /** * 编写一个类,继承 ActionSupport * 如果要给指定的方法校验,需要按规则定义一个方法: * 校验的方法名要遵循:validate+方法名首字母大写 * 如下,要给 add 方法校验,定义的方法名就为 validateAdd * 然后每次请求这个方法就会限制性校验方法 * 如果验证有错误,将错误通过 this.addFieldError 或 this.addActionError 交给 Struts2 * 然后 Struts2 会返回 input 逻辑视图,手动定义好 input 跳转到的页面 * 在页面可以通过 <s:actionerror/> <s:fielderror/> 标签获取到错误信息 */ public void validateAdd() { System.out.println("from validateAdd..."); } }
例 2:对 Action 中指定方法进行校验
配置文件方式
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <!-- 1、在 Action 所在包下创建一个 xml 文件,名称为 Action类名-validation.xml 如 : 此文件是给名为 Test3Action 的 Action 校验,文件名则为 Test3Action-validation.xml 2、引入 DTD 约束,该约束可在 xwork-core-2.3.37.jar!/xwork-validator-1.0.3.dtd 下找到 --> <validators> <!-- name : 要校验的字段名 注意:需要获取到值才能对字段进行校验,所以在 Action 中要给对应字段提供 get 方法。 --> <field name="name"> <!-- type : Struts2 已经给我们提供了很多验证器 在 xwork-core-2.3.37.jar!/com/opensymphony/xwork2/validator/validators/default.xml 中可以看到 --> <field-validator type="requiredstring"> <!--返回的错误信息--> <message>用户名不能为空</message> </field-validator> </field> </validators>
例 1:对整个 Action 进行校验
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <!-- 1、在 Action 所在包下创建一个 xml 文件,名称为 Action类名-方法访问路径-validation.xml 如 : 此文件只给名为 Test4Action 的 Action 下的 add 方法校验, Struts.xml 对应Action 配置为 <action name="*_*" class="com.zze.action.{1}Action" method="{2}"> 文件名则为 Test4Action-Test4_add-validation.xml 2、引入 DTD 约束,该约束可在 xwork-core-2.3.37.jar!/xwork-validator-1.0.3.dtd 下找到 --> <validators> <!-- name : 要校验的字段名 注意:需要获取到值才能对字段进行校验,所以在 Action 中要给对应字段提供 get 方法。 --> <field name="name"> <!-- type : Struts2 已经给我们提供了很多验证器 在 xwork-core-2.3.37.jar!/com/opensymphony/xwork2/validator/validators/default.xml 中可以看到 --> <field-validator type="stringlength"> <param name="minLength">6</param> <param name="maxLength">12</param> <!--返回的错误信息--> <message>用户名必须在6-12位之间</message> </field-validator> </field> </validators>
例 2:对 Action 中指定方法进行校验
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator Definition 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd"> <validators> <!--必填校验器,要求被校验的属性值不能为 null--> <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> <!--必填字符串校验器,要求被校验的属性值不能为 null,并且长度大于 0 ,默认情况下不会对字符串去前后空格--> <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/> <!--数值校验器,要求被校验的属性值可转 int ,且可指定范围,min 指定最小值,max 指定最大值--> <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/> <!--数值校验器,要求被校验的属性值可转 long,且可指定范围,min 指定最小值,max 指定最大值--> <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/> <!--数值校验器,要求被校验的属性值可转 short,且可指定范围,min 指定最小值,max 指定最大值--> <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/> <!--数值校验器,要求被校验的属性值可转 double,且可指定范围,min 指定最小值,max 指定最大值--> <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/> <!--日期校验器,要求被校验的属性值可转 Date,且可指定范围,min 指定最小值,max 指定最大值--> <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/> <!--OGNL表达式校验器,它是一个非属性校验器,expression 指定 ognl 表达式,该逻辑表达式基于 ValueStack进行求值,返回 true 时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中--> <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/> <!--字段OGNL表达式校验器,要求被校验的属性值满足一个 OGNL 表达式,expression 参数指定 OGNL 表达式,该逻辑表达式基于 ValueStack进行求值,返回 true 时校验通过,否则不通过--> <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/> <!--邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址--> <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/> <!--网址校验器,要求如果被校验的属性值非空,则必须是合法的 url 地址--> <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/> <!--用于校验 Action 中符合类型的属性,它指定一个校验文件用于校验复合类型属性中的属性--> <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/> <!--转换校验器,指定在类型转换失败时,提示的错误消息--> <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/> <!--字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength 指定最小长度,maxLength 指定最大长度,trim 指定交验之前是否去除字符串前后空格--> <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/> <!--正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression 指定正则表达式,caseSensitive 指定进行正则表达式匹配时,是否区分大小写,默认为 true--> <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/> <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/> </validators>
xwork-core-2.3.37.jar!/com/opensymphony/xwork2/validator/validators/default.xml
自定义校验器
package com.zze.validator; import com.opensymphony.xwork2.validator.ValidationException; import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport; /** * 对指定字段进行过滤 * * 除了继承 FieldValidatorSupport * 还可以继承 ValidatorSupport */ public class AgeValidator extends FieldValidatorSupport { @Override public void validate(Object object) throws ValidationException { // 获得字段名称 String fieldName = this.getFieldName(); Object fieldValue = this.getFieldValue(fieldName, object); if(fieldValue instanceof Integer){ int age = (Integer)fieldValue; if(age<0){ this.addFieldError(fieldName,object); } } } }
com.zze.validator.AgeValidator:自定义的校验器
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator Definition 1.0//EN" "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd"> <!--在 src 下新建 validators.xml --> <validators> <!--注册验证器--> <validator name="ageValidator" class="com.zze.validator.AgeValidator"/> </validators>
validators.xml:注册验证器
国际化
全局国际化
1、在类路径下新建 properties 资源文件,文件名为如下格式:
名称_en_uS.properties // 英文 名称_zh_CN.properties // 中文
例如:
name=姓名不能为空
message_zh_CN.properties
name=name can't be null
message_en_US.properties
2、在 struts.xml 中配置常量:
<constant name="struts.custom.i18n.resources" value="message"/>
3、接下来就可以获取资源文件中国际化后的内容了:
package com.zze.action; import com.opensymphony.xwork2.ActionSupport; public class I18NAction extends ActionSupport { @Override public String execute() throws Exception { String name = getText("name"); System.out.println(name); // 姓名不能为空 return super.execute(); } }
在 Action 中获取
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>国际化测试</title> </head> <body> <s:text name="name"/> </body> </html>
在 JSP 中获取
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <validators> <field name="name"> <field-validator type="requiredstring"> <!--通过 key 取出国际化文本--> <message key="name"></message> </field-validator> </field> </validators>
配置文件中获取
Action范围国际化
1、在 Action 所在的包下创建 properties 资源文件,文件名为如下格式:
Action名_zh_CN.properties // 中文 Action名_en_US.properties // 英文
2、直接在 Action 中使用即可,使用方式同全局一致。
包范围国际化
1、在需国际化的包下新建 properties 资源文件,文件名格式如下:
package_zh_CN.properties // 中文 package_en_US.properties // 英文
2、接下来在当前包及子包中都能使用该国际化资源文件。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>国际化测试</title> </head> <body> <s:i18n name="com/zze/action/package"> <s:text name="msg"/> </s:i18n> </body> </html>
JSP 中获取
使用占位符
wel=欢迎 {0}
com/zze/action/package_zh_CN.properties
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>国际化测试</title> </head> <body> <s:i18n name="com/zze/action/package"> <s:text name="wel"> <s:param>张三</s:param> </s:text> </s:i18n> </body> </html>
JSP 中传参并获取
package com.zze.action; import com.opensymphony.xwork2.ActionSupport; public class I18NAction extends ActionSupport { @Override public String execute() throws Exception { String wel = getText("wel", new String[]{"张三"}); System.out.println(wel); // 欢迎 张三 return super.execute(); } }
Action 中传参并获取
文件上传
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!--限制单次文件上传总大小不可大于 5m--> <constant name="struts.multipart.maxSize" value="5242880"/> <!--配置国际化资源文件名--> <constant name="struts.custom.i18n.resources" value="message"/> <package name="test" extends="struts-default" namespace="/"> <action name="upload" class="com.zze.web.action.FileUploadAction"> <result name="input">/index.jsp</result> <interceptor-ref name="defaultStack"> <!--限制单个文件大小不超过 2m--> <param name="fileUpload.maximumSize">2097152</param> <!--限制文件后缀--> <param name="fileUpload.allowedExtensions">.jpg,.png</param> <!--限制文件的 MIME 类型--> <param name="fileUpload.allowedTypes">image/jpg,image/png</param> </interceptor-ref> </action> </package> </struts>
struts.xml
struts.messages.error.uploading=\u4e0a\u4f20\u9519\u8bef struts.messages.error.file.too.large=\u6587\u4ef6\u592a\u5927 struts.messages.error.content.type.not.allowed=\u8bf7\u9009\u62e9\u56fe\u7247\u6587\u4ef6 struts.messages.error.file.extension.not.allowed=\u8bf7\u9009\u62e9\u002e\u006a\u0070\u0067\u6216\u002e\u0070\u006e\u0067\u7ed3\u5c3e\u7684\u6587\u4ef6
message_zh_CN.properties
package com.zze.web.action; import com.opensymphony.xwork2.ActionSupport; import org.apache.commons.io.FileUtils; import org.apache.struts2.interceptor.ServletResponseAware; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.File; public class FileUploadAction extends ActionSupport implements ServletResponseAware { /* 为文件上传提供三个属性 : String 表单项 name + FileName : 接收文件名称 File 表单项 name : 接收文件内容 String 表单项 name + ContentType : 上传文件的 ContentType */ private String fileFileName; private File file; private String fileContentType; public void setFileFileName(String fileFileName) { this.fileFileName = fileFileName; } public void setFile(File file) { this.file = file; } public void setFileContentType(String fileContentType) { this.fileContentType = fileContentType; } private ServletResponse response; @Override public String execute() throws Exception { response.setContentType("text/plain;charset=utf8"); String msg = "上传成功"; System.out.println(fileFileName); System.out.println(file); System.out.println(fileContentType); // 存储路径 String fullPath = "D://upload/"+fileFileName; File destFile = new File(fullPath); FileUtils.copyFile(file,destFile); response.getWriter().write(msg); return NONE; } @Override public void setServletResponse(HttpServletResponse response) { this.response = response; } }
com.zze.web.action.FileUploadAction
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Struts2 文件上传测试</title> </head> <body> <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="上传"> <s:actionerror/> <s:fielderror/> </form> </body> </html>
index.jsp