在前面的一篇文章中,使用的是官方例子,这里对其进行一个分析学习。
这个例子是基于Struts2的,和Struts1有区别。
代码的整体结构如下
先从配置文件入手,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.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error"/>
</global-exception-mappings>
<action name="index">
<result type="redirectAction">
<param name="actionName">HelloWorld</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
<include file="example.xml"/>
<!-- Add packages here -->
</struts>
有include的xml文件,example.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>
<package name="example" namespace="/example" extends="default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.jsp</result>
</action>
<action name="Login_*" method="{1}" class="example.Login">
<result name="input">/example/Login.jsp</result>
<result type="redirectAction">Menu</result>
</action>
<action name="*" class="example.ExampleSupport">
<result>/example/{1}.jsp</result>
</action>
<!-- Add actions here -->
</package>
</struts>
在上面可以看到通配符*,支持通配符
<action name="Login_*" method="{1}" class="example.Login">
<result name="input">/example/Login.jsp</result>
<result type="redirectAction">Menu</result>
</action>
想必:Login_execute, Login_test 等Action名称, 而method="{1}" 中的{1} 应该是 第一个通配符的值, 例如execute, test, 也就是说要执行java 类Login 中的execute,input方法。
实验一下:http://localhost:8080/Struts2Demo/example/Login_execute, 注意namespace 是/example
果然登录界面出现了:
上面的...required是必然出现的,因为检查为false
测试一下 http://localhost:8080/Struts2Demo/example/Login_test
由于没有test方法,所以肯定抛异常,这个异常是自己定义的啦
<global-results>
<result name="error">/error.jsp</result>
</global-results>
这里的error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Simple jsp page</title>
</head>
<body>
<h3>Exception:</h3>
<s:property value="exception" />
<h3>Stack trace:</h3>
<pre>
<s:property value="exceptionStack" />
</pre>
</body>
</html>
但是,上面的一篇文章中有说ActionForm的,但是在哪里啊,没有找到。
原来,Struts2中有进步的啦。
在Struts1 中,是使用ActionForm 来封装从浏览器中来的数据,每个Action 都需要写一个ActionForm,, 用起来虽然比较麻烦,但是比较清晰。
在Struts2中,我们似乎并没有看到类似的概念,只看到在Action中的一些getter和setter方法,例如class Login 中的username,password
/*
* $Id: Login.java 471756 2006-11-06 15:01:43Z husted $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package example;
public class Login extends ExampleSupport {
public String execute() throws Exception {
if (isInvalid(getUsername())) return INPUT;
if (isInvalid(getPassword())) return INPUT;
return SUCCESS;
}
private boolean isInvalid(String value) {
return (value == null || value.length() == 0);
}
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
在Login.jsp中,肯定有与其相对应的类似javabean的东西
<s:form action="Login">
<s:textfield key="username"/> <--- 肯定是和class Login 中的username 绑定
<s:password key="password" /> <--- 肯定是和class Login 中的password 绑定
<s:submit/>
</s:form>
我们在setUsername和setPassword中,可以加入一些System.out函数,来看一下何时调用。
在Struts2中, Form 和 Action是混到一起了,Action中可以有实例变量,并且可以被框架自动赋值(当然是通过setter方法),至于通过getter暴露出的属性值,在jsp中可以引用。
既然Action中有实例变量,那就意味着Struts2会为每一个http request 创建一个新的Action实例(Struts1不是这样的),所以你不用担心线程安全问题。
为什么要混合ActionForm和Action?
为每个Action开发一个ActionForm确实是一件不那么Happy的事情,需要额外的工作建立一个额外ActionForm的类, 并且这个类只能用在相关的Action上。Struts2 则把Action和ActionForm合并为一了, 咋一看感觉比较乱,但用起来就会发现,确实是极大的减少了开发量,通过简单的getter/setter就能获取界面上的数据。
虽然在设计上看起来不够优雅和完美,但实用性十足,这就够了。
对于那些追求完美的同学, 我相信struts2 还是能支持ActionForm 的。
如何做输入验证?
就是简单的一个xml文件中验证的啦,在Login_validator.xml文件中,
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<message key="requiredstring"/>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message key="requiredstring"/>
</field-validator>
</field>
</validators>
1. 有两个field,肯定是验证两个的啦。
2. 然后,field-validator的type是requiredstring,就是说需要字符串类型的啦,也不想要null
3. 如果失败,需要message,这里的massage是key=。。。所以,一定是个键值,在resource中有定义的。
4. 估计也是根据名字来进行统配的。<ActionName>_validator.xml,所以是和Action相配的,需要在同一个package下面。
HelloWorld.message= Struts is up and running ...
requiredstring = ${getText(fieldName)} is required.
password = password
username = username
Missing.message = This feature is under construction. Please try again in the next interation.
上面就是有个requiredstring的键值,
${getText(fieldName)} is required
其中,
fieldName 应该就是当前要验证的filed的名称,例如"username", "password"
而getText() 应该是struts2提供的方法,估计是为了多语言处理的,如果当前的field 是"username" , 那么 getText(“username”) 在这个例子中就是“User Name”.
错误信息怎么显示?
验证失败,肯定要在jsp 中输入错误信息,这一点Struts2的tag library已经给我们封装好了, 当然是高度封装:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Sign On</title>
</head>
<body>
<s:form action="Login">
<s:textfield key="username"/>
<s:password key="password" />
<s:submit/>
</s:form>
</body>
</html>
如果username 或者password没有输入, struts2就会在每一个输入域的上方显示错误消息。
注意 <s:textfield key="username"/> 用的是key = xxx , 你可以试一下 name=xxxx, 就可以发现 其中的奥秘:
使用key =xxx , 最终的html中既有label ,又有input field, 使用name=xxx,只会输出 input filed ,需要自己来写label 了。
我只能通过xml文件类配置输入验证吗
当然不是,这个例子中就有的啦。重写方法validate。
观察一下Login这个类, 它最终扩展了ActionSupport ,这个类有很多和验证相关的方法,例如addFieldError (),validate()等等,我们尝试一下在class Login中覆盖validate方法:
public void validate(){
if(!"andy".equals(getUsername())){
addFieldError("username",getText("user.incorrect"));
}
if(!"pass4andy".equals(getPassword())){
addFieldError("password",getText("password.incorrect"));
}
}
当然要记得在package.properties 新添加两个message
user.incorrect=User name is not correct
password.incorrect=Password is not correct
当然,这里可以去查调用其它的,比如来查数据库的啦。
后记
部分参考 http://www.myexception.cn/program/1434295.html
谢谢