java框架之Struts2(4)-拦截器&标签库
拦截器
概述
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
java框架之Struts2(4)-拦截器&标签库的更多相关文章
- java框架篇---struts实现拦截器
Struts2的拦截器和Servlet过滤器类似.在执行Action的execute方法之前,Struts2会首先执行在struts.xml中引用的拦截器,在执行完所有引用的拦截器的intercept ...
- JavaWeb框架_Struts2_(三)---->Struts2的拦截器
2. Struts2的拦截器(使用拦截器实现权限控制) 2.1 拦截器的概述 拦截器是Struts2的核心组成部分,它可以动态的拦截Action调用的对象,类似与Servlet中的过滤器.Struts ...
- java之struts2之拦截器
1.struts2能完成数据的设置,数据的封装,数据的类型转换,数据的校验等等.struts2是如何来完成这些功能的?struts2的所有功能都是由拦截器来完成的. 2.拦截器是struts2的核心. ...
- Struts2【拦截器】就是这么简单
什么是拦截器 拦截器Interceptor.....拦截器是Struts的概念,它与过滤器是类似的...可以近似于看作是过滤器 为什么我们要使用拦截器 前面在介绍Struts的时候已经讲解过了,Str ...
- 简单理解Struts2中拦截器与过滤器的区别及执行顺序
简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...
- struts2总结六: Struts2的拦截器
一.Struts2的系统结构图
- Struts2使用拦截器完成权限控制示例
http://aumy2008.iteye.com/blog/146952 Struts2使用拦截器完成权限控制示例 示例需求: 要求用户登录,且必须为指定用户名才可以查看系统中某个视图资源:否 ...
- Struts2默认拦截器栈及内建拦截器使用具体解释
Struts2内建拦截器介绍: alias (别名拦截器):同意參数在跨越多个请求时使用不同别名,该拦截器可将多个Action採用不同名字链接起来,然后用于处理同一信息. autowiring ...
- (六)Struts2的拦截器
一.简介 拦截器体系是struts2重要的组成部分.正是大量的内建拦截器完成了该框架的大部分操作. 比如params拦截器将请求参数解析出来,设置Action的属性.servletConfig拦截器负 ...
随机推荐
- Quartz与Spring Boot集成使用
上次自己搭建Quartz已经是几年前的事了,这次项目中需要定时任务,需要支持集群部署,想到比较轻量级的定时任务框架就是Quartz,于是来一波. 版本说明 通过搜索引擎很容易找到其官网,来到Docum ...
- C语言 · 单词数统计
单词数统计 输入一个字符串,求它包含多少个单词. 单词间以一个或者多个空格分开. 第一个单词前,最后一个单词后也可能有0到多个空格. 比如:" abc xyz" 包含两个单词 ...
- Hlacon 之Image ,Region,XLD
一 读取的3种方式: read_image( image,'filename') //image 是输出对象,后面是输入文件的路径和名称 读取多图: 1,申明一个数组,分别保存路径 ImagePath ...
- Direct3D 11 Tutorial 3: Shaders and Effect System_Direct3D 11 教程3:着色器和效果系统
概述 在上一个教程中,我们设置了一个顶点缓冲区并将一个三角形传递给GPU. 现在,我们将逐步完成图形管道并查看每个阶段的工作原理. 将解释着色器和效果系统的概念. 请注意,本教程与前一个源代码共享相同 ...
- 遍历一个Set的方法只有一个:迭代器(interator)。
Set-HashSet实现类: 遍历一个Set的方法只有一个:迭代器(interator). HashSet中元素是无序的(这个无序指的是数据的添加顺序和后来的排列顺序不同),而且元素不可重复. 在O ...
- 集合的最大缺点是无法进行类型判定(这个缺点在JAVA1.5中已经解决),这样就可能出现因为类型不同而出现类型错误。
集合的最大缺点是无法进行类型判定(这个缺点在JAVA1.5中已经解决),这样就可能出现因为类型不同而出现类型错误. 解决的方法是添加类型的判断. LinkedList接口(在代码的使用过程中 ...
- Adding ASP.NET MVC5 Identity Authentication to an existing project
Configuring Identity to your existing project is not hard thing. You must install some NuGet package ...
- makefile 常用函数
Linux下编译c/c++源码需要编写makefile文件,文章参看 http://blog.sina.com.cn/s/blog_4c4d6e74010009jr.html 一函数的调用语法 二字符 ...
- Android基础开发归档
一.Android 基本组件汇总 1. Android中PackageManager使用示例 : http://blog.csdn.net/qinjuning/article/details/686 ...
- Qt编写自定义控件4-旋转仪表盘
前言 旋转仪表盘,一般用在需要触摸调节设置值的场景中,其实Qt本身就提供了QDial控件具有类似的功能,本控件最大的难点不在于绘制刻度和指针等,而在于自动计算当前用户按下处的坐标转换为当前值,这个功能 ...