前面说道实现Action一般选择继承ActionSupport的方式,因为它提供了一些额外的功能,比如基本的数据验证和访问本地信息。
基本数据验证
由于ActionSupport类实现了Validateable接口,那么在该动作被触发的时候会在执行动作方法之前先执行validate方法,如果验证没有通过,那么就会返回信息输入结果页面。因此我们只需要在Action中重写validate方法就可以实现数据的验证了。
public class HelloWorld extends ActionSupport {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String execute1() throws Exception {
return "success";
}
@Override
public void validate() {
if(userName == null){
addFieldError("userName","未获取到参数!");
}else if(userName.length()>5){
addFieldError("userName","用户名太长");
}
}
}
input.jsp
<body>
<s:if
test="hasFieldErrors()">
<s:iterator
value="fieldErrors">
<font
color="#FF0000"><s:property
value="value[0]"/></font><br>
</s:iterator>
</s:if>
<form
action="login.action"
method="post">
username : <input
type="text"
name="userName"/><br/>
password :<input
type="password"
name="password"><br/>
<input
type="submit"
value="submit"/>
</form>
</body>
执行:
结果:
这里有几点需要注意:
(1) 每次执行动作方法之前,都会执行validate方法,如果我们的Action中有多个动作方法的话,那么每个动作方法执行之前都会执行validate方法,因此validate方法中一般执行一些通用的检查。
(2) 如果validate中没有通过,即产生了错误消息,那么不会执行动作方法,会直接返回”input”。
下面大致了解一下validate方法执行的原理。首先我们需要知道,这个方法是被拦截器调用的,拦截器放在动作执行之前,拦截每个访问该Action的请求。这个拦截器叫做Workflow拦截器,查看文档可以知道,该拦截器的实现类为com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor。文档中说这个拦截器要做的事情:
(1)如果Action中有validate{MethodName}()方法,那么执行它
(2)如果(1)不成立,但是Action中有validateDo{MethodName}()方法,那么执行它
(3)不管(1)或(2)是否执行,只要这个拦截器的alwaysInvokeValidate属性为true,那么总是会执行validate方法。
查看DefaultWorkflowInterceptor的源码:
public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {
private static final long serialVersionUID = 7563014655616490865L;
private static final Logger LOG = LoggerFactory.getLogger(DefaultWorkflowInterceptor.class);
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
private String inputResultName = Action.INPUT;
public void setInputResultName(String inputResultName) {
this.inputResultName = inputResultName;
}
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware validationAwareAction = (ValidationAware) action;
if (validationAwareAction.hasErrors()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Errors on action " + validationAwareAction + ", returning result name 'input'");
}
String resultName = inputResultName;
if (action instanceof ValidationWorkflowAware) {
resultName = ((ValidationWorkflowAware) action).getInputResultName();
}
InputConfig annotation = action.getClass().getMethod(invocation.getProxy().getMethod(), EMPTY_CLASS_ARRAY).getAnnotation(InputConfig.class);
if (annotation != null) {
if (!annotation.methodName().equals("")) {
Method method = action.getClass().getMethod(annotation.methodName());
resultName = (String) method.invoke(action);
} else {
resultName = annotation.resultName();
}
}
return resultName;
}
}
return invocation.invoke();
}
}
发现在这个拦截器中根本就没有调用validate方法,而只是对是否产生的错误信息进行了检测。并且看以看到,如果存在错误信息,默认返回的Result是Action.input(”input”)。
那么既然workflow拦截器没有执行validate方法,由于我们的Action使用的默认的拦截器栈,那么就去看看在workflow拦截器前面的拦截器validation拦截器。
查看文档可以知道这个拦截器的实现类为:org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor。这个类继承了com.opensymphony.xwork2.validator.ValidationInterceptor这个类查看这个类的源码,里面有一个doBeforInvocation方法:
protected void doBeforeInvocation(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
ActionProxy proxy = invocation.getProxy();
//the action name has to be from the url, otherwise validators that use aliases, like
//MyActio-someaction-validator.xml will not be found, see WW-3194
String context = proxy.getActionName();
String method = proxy.getMethod();
if (log.isDebugEnabled()) {
log.debug("Validating "
+ invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName() + " with method "+ method +".");
}
if (declarative) {
if (validateAnnotatedMethodOnly) {
actionValidatorManager.validate(action, context, method);
} else {
actionValidatorManager.validate(action, context);
}
}
if (action instanceof Validateable && programmatic) {
// keep exception that might occured in validateXXX or validateDoXXX
Exception exception = null;
Validateable validateable = (Validateable) action;
if (LOG.isDebugEnabled()) {
LOG.debug("Invoking validate() on action "+validateable);
}
try {
PrefixMethodInvocationUtil.invokePrefixMethod(
invocation,
new String[] { VALIDATE_PREFIX, ALT_VALIDATE_PREFIX });
}
catch(Exception e) {
// If any exception occurred while doing reflection, we want
// validate() to be executed
if (LOG.isWarnEnabled()) {
LOG.warn("an exception occured while executing the prefix method", e);
}
exception = e;
}
if (alwaysInvokeValidate) {
validateable.validate();
}
if (exception != null) {
// rethrow if something is wrong while doing validateXXX / validateDoXXX
throw exception;
}
}
}
可以看到,正是在这个方法中对Action中的validate方法进行了调用。为了验证到底validate方法是在validation拦截器中被调用的还是在workflow拦截器中被调用的,我们写个小实例,不使用defaultStack,我们手动为Action配置拦截器栈(这个拦截器栈基本和defaultStack相同):
<package name="default" namespace="/" extends="struts-default">
<interceptors>
<interceptor-stack name="luo">
<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="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="luo"></default-action-ref>
<action name="hello" class="action.HelloWorld">
<result name="success">/success.jsp</result>
<result name="input">/input.jsp</result>
</action>
</package>
我们仅仅是将validation拦截器移出了Action的先前默认的defaultStack拦截器栈中,我们再来执行前面执行过的测试:
结果:
success结果被显示了,说明validate方法没有被调用了。那么就说明了validate方法是在validattion拦截器中被调用的。
workflow拦截器和validation拦截器可以传递一些参数:
<interceptor-ref name="validation">
<paramname="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
当执行到与excludeMethods参数中指定的方法同名的方法时,拦截器不会做出反映。还可以通过这种形式来为拦截器指定其他属性,比如为workflow指定默认返回的Result等。
<interceptor-ref name="workflow">
<param name="inputResultName">error</param>
<param name="excludeMethods">*</param>
<param name="includeMethods">myWorkflowMethod</param>
</interceptor-ref>
访问本地信息
在上面的例子程序中,我们将错误信息硬编码在代码中,这样有一些弊端:
(1) 一是不容易修改,如果我们想改变消息的内容,还得重新修改代码和重新编译类
(2) 而是不利于国际化,如果要将其中的中文换成英文,那么还得到代码中来修改和重新编译类
通过访问本地信息,我们将一些消息按照键值对的形式保存在类文件外部的文件,通过key来获取文件中对应的消息,那么一旦我们需要修改消息内容,那么就只需要修改这个外部文件而不需要重新编译类了。基本步骤:
(1) 首先建立消息文件,在Action类的类路径下建立一个与Action同名的properties文件,例如HelloWorld.properties,然后在文件里按照 key= value的形式添加错误消息。
nameIsNull =\u672A\u83B7\u53D6\u5230\u53C2\u6570
nameIsTooLong=\u7528\u6237\u540D\u592A\u957F
需要注意的是Value的值必须是unicode编码,这样在程序充才能正确获取,在eclipse中可以使用可视化工具编辑properties文件,这样生成的代码会将中文转换为unicode编码。JDK中也提供了native2ascii命令来实现这种转换,具体方法google一下就知道了。
(2)修改原来在Actin中硬编码的错误消息,改成从配置文件中读取信息。
public
void validate() {
if(userName ==null){
addFieldError("userName",getText("nameIsNull"));
}else
if(userName.length()>5){
addFieldError("userName",getText("nameIsTooLong"));
}
}
重新运行程序,结果和前面一样。至于实现的原理,最终还是借助JDK提供的ResourceBoundle来实现的。ActionSupport类实现了TextProvider接口和LocalProvider接口。当我们调用ActionSupport的getText方法时,其内部实际上是调用了TextProvider一个实现类(TextProviderSupport)的对象上的getText方法。
public String getText(String aTextName) {
return getTextProvider().getText(aTextName);
}
private TextProvider getTextProvider() {
if (textProvider ==null) {
TextProviderFactory tpf = new TextProviderFactory();
if (container !=null) {
container.inject(tpf);
}
textProvider = tpf.createInstance(getClass(), this);
}
return textProvider;
}
由于实现了LocalProvider,因此ActionSupport也是LocalProvider类型,其getLocal从ActionContext中获取Local对象。使用系统默认的Local对象时,ResourceBoundle会在Class所在路径下寻找与当前类同名的properties文件,如果使用指定的Local对象,那么我们的资源文件还需要在名字中添加相应的国家代码,例如:HelloWorld_zh_CN.properties
分享到:
相关推荐
在Struts2中,Model通常由业务对象(BO)或数据访问对象(DAO)组成,View负责展示数据,Controller(Action)处理用户请求并调用Model更新数据,再将结果传递给View。 三、配置与拦截器 1. 配置:Struts2的配置...
13. ** strut2四天笔记**:这份学习笔记可能涵盖了以上所有知识点,包括如何创建Action,配置struts.xml,使用OGNL表达式,处理异常,以及实践中的各种技巧和最佳实践。 在四天的学习过程中,你应该通过实践和理解...
根据给定的文件信息,以下是对Struts2学习笔记中涉及的关键知识点的详细解析: ### Struts2框架概览 #### MVC模式的理解与演进 Struts2是基于MVC(Model-View-Controller)模式设计的一种Java Web开发框架。在MVC...
OGNL是Struts2中用于对象属性访问和表达式语言,它允许在Action、ValueStack和视图层之间传递数据。ValueStack是一个对象栈,它持有ActionContext上下文中的所有对象,包括Action实例、ActionForm、Session、Request...
### Struts2 学习重点知识点总结 #### 一、Struts2 概念与架构 **1.1 Struts2 简介** - **定义**:Struts2 是 Apache 组织提供的一个基于 MVC 架构模式的开源 Web 应用框架。 - **核心**:Struts2 的核心其实是 ...
### Struts2学习笔记知识点概览 #### 一、环境搭建 **1.1 Struts2简介** - **Struts2概述**:Struts2是一个开源的MVC框架,它结合了Struts 1.x、WebWork和其他一些框架的优点。Struts2的主要目标是简化Web应用程序...
张龙圣思园的Struts2学习笔记,无疑为Java开发者提供了一份宝贵的参考资料,它可能涵盖了Struts2的基础概念、核心组件、配置方式以及实战技巧。 首先,让我们深入了解Struts2的核心特性。Struts2是MVC(Model-View-...
### Struts2学习笔记知识点详解 #### 一、Struts2框架的基本引入步骤 ##### 1. 导入Struts2相关Jar包 在引入Struts2框架时,首先需要将Struts2的相关Jar包导入到项目的类路径中。这些Jar包通常包括核心库以及其他...
### Struts2学习笔记之文件上传与Ajax开发 #### Struts2文件上传 **文件上传简介** 文件上传是Web应用中常见的功能之一,Struts2框架内置了对文件上传的支持,使得开发者能够轻松地实现这一功能。为了确保文件...
2. **模型驱动**:使用一个公共对象作为模型,Action类持有这个模型的引用,Struts2将请求参数填充到模型对象中,这种方式更便于数据管理和共享。 此外,Struts2还支持动态方法调用、国际化、主题和模板、AJAX集成...
Namespace在Struts2中主要用于组织和隔离Action,它决定了Action的访问路径。默认情况下,Namespace为空字符串,这意味着任何没有明确Namespace的Action都可以被全局访问。Namespace可以设置为“/”或“/xxx”或“/...
在Struts2中,学习笔记通常会涵盖以下几个关键概念: 1. **源代码查看和Javadoc**:开发者可以通过查看源代码来理解Struts2的工作原理,而Javadoc则提供了API文档,帮助理解类和方法的功能。 2. **包(Package)和...
在**牧紫小岩的Struts2学习笔记**中,提到了Struts1和Struts2在实例化Action上的关键差异。Struts1在启动时创建Action实例,并在整个应用程序运行期间复用该实例,这意味着所有用户请求共享同一实例,这可能导致线程...
这份"struts2学习笔记和源码"资源是学习这个框架的理想材料,特别适合初学者。 一、Struts2框架基础 Struts2是Apache软件基金会下的一个项目,它是Struts1的升级版,弥补了Struts1的一些不足,如动作映射和拦截器等...