Struts2
struts2的mvc架构(真正意义上的MVC架构)
是控制层去调用javaBean对象上的方法,由javaBean对象去操作Service层
Struts2的原理图
Struts2的特点
Struts2中的EL表达式
在struts2中的很多地方都可以用EL表达式(如JSP和配置文(包括验证器等的配置文件)件中)如果在JSP页面中使用EL表达式那么可以获取动作类中属性的值,如果在配置文件中使用EL表达式可以获取相关联动作类中属性的值
Struts2中动作类存放的位置及命名规范
在Struts2中的动作类一般放在报名为action的包下,并且这个动作类的名称一般遵循以Action字符结尾的命名规范
动作类一般继承ActionSupport这个类,并实现Serializable接口(此接口是对象序列化的接口)
动作方法及注意事项
动作方法就是当请求一个动作时所调用动作类中的方法。动作类中的动作方法时当动作类中的属性被注入完成后才会被调用,动作方法中可以直接与service层交互。
搭建Struts2的开发环境
一、搭建Struts2环境时,一般需要以下几个步骤:
1、 找到开发Struts2应用需要使用到的jar文件
可以到http://struts.apache.org/download.cgi#struts2014 下载struts-2.x.x-all.zip最新版压缩包,开发struts2应用需要依赖的jar包在界面目录的lib文件夹下。下面就是开发struts2最基本的jar包,每个版本最基本的jar包是不一样的,请参考不同版本的struts2的文档
struts-2.1.7最基本的jar包:
struts2-core.jar 核心jar包
xwork-2.jar xwork 核心jar包
ognl.jar ognl表达式
freemarker.jar FreeMarker模板
commons-logging.jar 日志
commons-fileupload.jar 文件上传
commons-io.jar 文件上传依赖的包
2、 编写Struts2的配置文件
先将配置文件的dtd约束文件导入进来,放在WEB-INF目录下的dtds目录下(位置可以任意),约束文件可以再struts-2.x.x-all.zip的包中搜索dtd的文件,然后拷贝需要的dtd约束文件版本(这里是struts-2.1.7.dtd)
然后再创建一个名称为struts.xml的xml配置文件,该文件需要放在WEB-INF/classes目录下(对应的开发环境目录就是src目录下)
此struts.xml配置文件的模板为:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
</struts>
struts.xml配置文件编写是没有提示的问题?
方法一:上网即可
方法二:
1、拷贝http://struts.apache.org/dtds/struts-2.1.7.dtd地址
2、Eclipse的window、preferences,搜索XML Catalog
3、点击add按钮
Location:dtd文件的路径
Key Type:URI(如果是Schema约束文件,就选“Schema location”)
Key:http://struts.apache.org/dtds/struts-2.1.7.dtd(这个就是这个dtd文家的URI)
3、 在web.xml文件中加入Struts2MVC框架启动配置(struts的过滤器)
在web.xml配置文件中配置struts2的过滤器(核心控制器)
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在struts1中,struts框架是通过servlet启动的,在struts2中,struts框架是通过Filter启动的
在StrutsPrepareAndExecuteFilter的init()初始化方法中将会去读类路径(WEB-INF/classes/struts.xml,就是上面的配置文件)下默认的配置文件struts.xml完成初始化操作
注意:struts2读取到struts.xml的内容后,以javabean形式存放在内存中.以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件(所以每次修改完此struts.xml配置文件后都必须重启服务器,(也可以重新发布)),(但也可修改struts2-core-2.1.8.1.jar包中org.apache.struts2包下的default.properties配置文件中的struts.devMode属性的值为true,表示当修改了此struts.xml配置文件时就重写发布此应用。修改此属性的方式:在struts.xml配置文件中的struts标签元素下添加一个<constant name="struts.devMode" value="true"></constant>这样就修改了struts.devMode属性的值了)
一个简单的struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts><!-- 这是struts2配置文件的根目录 -->
<!--
pageckage:方便管理动作元素
name:必须有。包的名称,配置文件中必须保证唯一。
namespace:该包的名称空间,一般是以"/"开头(就是通过浏览器访问的目录)
extends:继承的父包的名称。struts-default名称的包是struts2框架已经命名好的一个包。(在struts2-core.jar中有一个struts-default.xml中)
abstract:是否是抽象包。没有任何action标签元素的package包就是抽象包
-->
<package name="scxh" namespace="/test" extends="struts-default">
<!--
action:代表一个请求动作
name:同包中必须唯一。动作的名称(通过浏览器访问的资源)
class:负责处理的JavaBean的类全名(当访问此资源时实例化的javaBean类),此类的成员变量,会以映射关系的形式存储到域中
method:JavaBean中的对应处理方法。(动作方法:特点是,返回类型为String 并且是无参的)
-->
<action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="sayHello">
<!--
result:结果类型
name:动作方法返回的字符串
type:到资源地址的方式(默认值为转发:dispatcher,下面有讲type的取值)
主体内容:显示的jsp的具体地址
-->
<result name="success">/1.jsp</result>
</action>
</package>
<!-- 当访问http://localhost:8080/test/helloworld 这是资源时,会实例化cn.itcast.action.HelloWorldAction这个javaBean,
并会调用此javaBean中的sayHello()无参并且返回类型为String 的动作方法,如果返回值为success的话,就转发到/1.jsp页面上去 -->
</struts>
4、 指定struts2处理的请求后缀
默认的后缀名为:”action”和空的后缀名,表示如果请求资源的后缀名是action或没有后缀名的话,就通过struts2的过滤器进行处理,如果是其他的后缀名struts2就不处理
访问helloworld动作的方式:http://localhost:8080/struts2day01/test/helloworld 应用名称/包的名称空间/动作的名称
默认情况下:访问动作名helloworld,可以直接helloworld,或者helloworld.action(可以在struts.xml配置文件中的struts根元素下添加一个
<constant name="struts.action.extension" value="action,,do"></constant>标签 来修改其struts2-core-2.1.8.1.jar包中org.apache.struts2包下的default.properties配置文件中struts.action.extension属性的值。中间的空值代表没有后缀名时,也能访问得到)
5、action的名称搜索顺序
如果在访问的url的包的名称空间后面加上 任意的名称空间,都能访问到此资源,如:
http://localhost:8080/struts2day01/test/a/b/c/helloworld
/test/a/b/c:名称空间
helloworld:动作名称
搜索顺序:名称空间
/test/a/b/c/ 没有helloworld
/test/a/b/ 没有helloworld
/test/a/ 没有helloworld
/test/ 有了,调用执行
二、Struts2配置文件中的默认值
package 元素属性的默认值
name: (必须有)包的名称,配置文件中必须保证唯一。
namespace: (如果不写,在浏览器访问时不写此包的名称空间即可。)该包的名称空间,一般是以"/"开头(就是通过浏览器访问的目录)
extends: (必须写)继承的父包的名称。struts-default名称的包是struts2框架已经命名好的一个包。(在struts2-core.jar中有一个struts-default.xml中)
abstract: (默认值是false)是否是抽象包。没有任何action标签元素的package包就是抽象包
action 元素属性的默认值
name: (必须有) 同包中必须唯一。动作的名称(通过浏览器访问的资源)
class: 可以不写,默认值是com.opensymphony.xwork2.ActionSupport
method: 可以不写,默认调用的是execute(此方法在ActionSupport类中有,并且此方法的返回值就是”success”)
result 元素属性的默认值
name: 可以不写,默认值是”success”
结果处理器(重定向及转发的参数传递:请参考struts2.1_param工程)
一般在写负责处理的JavaBean时,此类一般都继承com.opensymphony.xwork2.ActionSupport这个类(此类中有execute方法,当method为空时,调用的就是execute方法)
result元素中type属性的取值(结果处理器)
struts2-core-2.1.8.1.jar包中的 /struts-default.xml中的package元素中的result-types元素中有定义:
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
dispatcher: 表示转发(默认值),转发到某个页面
chain: 转发到某个动作名称也就是跳转到某个已有的命名空间上去
如:
<package name="scxh1" namespace="/test" extends="struts-default">
<action name="a1">
<result name="success" type="chain">a2</result><!--当在同一个包(package)中这个动作请求要转发到另一个动作请求上面去,就需要用到chain转发-->
</action>
<action name="a2">
<result name="success" type="dispatcher">/2.jsp</result>
</action>
</package>
如果要转发的动作请求不是在同一个包(package)中(就是从一个名称空间中跳转到另一个名称空间中去),就需要用以下的方法了:
<package name="scxh1" namespace="/test1" extends="struts-default">
<action name="a1">
<result name="success" type="chain">
<param name="namespace">/test2</param><!—去访问test2名称空间中的a1动作请求 -->
<param name="actionName">a1</param>
</result>
</action>
</package>
<package name="scxh2" namespace="/test2" extends="struts-default">
<action name="a1">
<result name="success" type="dispatcher">/2.jsp</result>
</action>
</package>
redirect: 表示重定向到某个页面
redirectAction: 表示重定向到某个已有的命名空间上去(和上面的转发是一样的,只不过这里是重定向)
plainText: 表示以纯文本的形式输出jsp(就是输出我们在编辑jsp的内容,原样输出)(如果要设置编码,此转发处理器的类的charSet属性注入编码格式即可,而转发的地址可以给此location属性注入值)
stream: 表示返回一个流(在文件下载时需要用到)
当要下载一个文件时,就需要用此stream结果处理器了:
<package name="load" namespace="/load" extends="struts-default">
<action name="loadFile" class="cn.domain.UserAction" method="loadFile">
<result name="downloadOK" type="stream"><!-- 以下都是向此stream结果处理器对应的类中注入值,此结果处理器的注册在struts2-core.jar包中的struts-default.xml文件中 -->
<param name="contentType">${fileType}</param><!-- 注入文件的类型(重动作类中的成员变量中引用的) -->
<param name="contentLength">${fileLength}</param><!-- 注入文件的大小(重动作类中的成员变量中引用的) -->
<param name="inputName">imageInputStream</param><!-- 注入动作类中此文件输入流的名称(数据源流) -->
<param name="contentDisposition">attachment;filename="${fileName}"</param><!-- 设置contentDisposition响应头的值 -->
</result>
</action>
</package>
利用action元素中的param标签动态的为动作类(就是action标签中class属性的值的类,此类必须是一个javaBean对象)中的属性赋值和利用${属性名 }进行获取其属性的值
向操作类中的属性注入值,或用${属性名 }的方式获取此属性的值
<struts><!-- 这是struts2配置文件的根目录 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<package name="scxh" namespace="/test1" extends="struts-default">
<action name="a1" class="cn.domain.JavaBean" method="execute">
<!-- 此处的值是通过cn.domain.JavaBean对象的setMessage()方法注入进去的 -->
<param name="message">这个值是通过配置文件注入进去的</param>
<!-- 此处${message}取的是cn.domain.JavaBean对象中getMessage() 方法的返回值 -->
<result name="success">/1.jsp?msg=${message}</result>
</action>
</package>
</struts>
在jsp中取出message的值和msg的值
<body>
111111111:${message }
<br/>
111111111:${param.msg }
</body>
配置包范围内的错误页面
在一个包中(package元素)中如果出了异常,可以在package标签下添加一个global-results标签,并配置一个结果处理器就OK了
如:
<package name="scxh" namespace="/test1" extends="struts-default">
<!-- 配置包范围类的错误页面,当此包中只要对应的操作方法,返回值是error,那么都会被此显示到error.jsp页面上面去 -->
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<action name="a1">
<result name="success">/index.jsp</result>
</action>
</package>
配置全局范围内的错误页面及package的继承性
<!-- 全局范围内的错误页面配置 -->
<package name="scxh1" namespace="/test1" extends="struts-default">
<global-results>
<result name="error">/error.jsp</result>
</global-results>
</package>
<!-- 当scxh2继承了scxh1这个包时,那么scxh2就有了scxh1的功能了 -->
<package name="scxh2" namespace="/test2" extends="scxh1">
<action name="a1" class="cn.domain.Test" method="exe">
<result name="success">/1.jsp</result>
</action>
</package>
捕获指定异常的配置(或参考struts2.21_package_Exception工程)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts><!-- 这是struts2配置文件的根目录 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<!-- 指定异常截获 -->
<package name="exceptionTest" extends="struts-default">
<global-results>
<!-- 错误页面再转发到exceptionAction的动作上 -->
<result name="errorCatch" type="chain">
<param name="actionName">exceptionAction</param>
</result>
</global-results>
<!-- 此标签是捕获异常的标签 -->
<global-exception-mappings>
<!-- 当继承此包的页面发生了Exception异常,就转发到上面的errorCatch错误结果处理器 -->
<exception-mapping exception="java.lang.Exception" result="errorCatch"></exception-mapping>
</global-exception-mappings>
<!-- 此动作用于捕获Exception -->
<action name="exceptionAction" class="cn.domain.ExceptionAction" method="execute">
<result name="success">/index.jsp</result>
</action>
</package>
<!-- 当此包中的页面只要发生java.lang.Exception异常,都会被上面的 global-exception-mappings标签捕获到-->
<package name="errorTest" extends="exceptionTest">
<action name="errorTest" class="cn.domain.UserAction">
<result name="success">/1.jsp</result>
</action>
</package>
</struts>
自定义结果处理器
1、 自定义结果处理器类应存放在名为dispatcher的包下,并且此类的类名应以”Result”结尾。
2、 自定义结果处理器类应继承StrutsResultSupport此抽象类,并重写抽象类中的doExecute方法。
3、 自定义结果处理器应在struts.xml文件中进行注册
示例:(创建一个自定的转发结果处理器)
自定义结果处理器类:
package cn.dispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import com.opensymphony.xwork2.ActionInvocation;
//定义一个自定的转发结果处理器
//自定义结果处理器类,必须继承StrutsResultSupport抽象类,并重写doExecute方法
public class UserDefinedResult extends StrutsResultSupport{
private static final long serialVersionUID = 1L;
@Override//第一个参数传递的是:result标签中的内容,第二个参数为:ActionInvocation对象,此对象可以获取到所有的东东
protected void doExecute(String finalLocation, ActionInvocation invocation)
throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
//转发到result标签中指定的页面去
request.getRequestDispatcher(finalLocation).forward(request, response);
}
}
struts.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
<!-- struts2自定义结果处理器 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<package name="result" abstract="true" extends="struts-default">
<!-- 注册此结果处理器 -->
<result-types><!-- 自定义转发结果处理器 -->
<result-type name="userDefinedDispatcher" class="cn.dispatcher.UserDefinedResult" />
</result-types>
</package>
<!-- http://localhost:8080/struts3.16_user_defined_result/testRsult 访问时的url -->
<package name="testRsult" extends="result">
<action name="testRsult">
<!-- 引用自定义的结果处理器(转发) -->
<result name="success" type="userDefinedDispatcher">/index.jsp</result>
</action>
</package>
</struts>
struts2的默认配置文件中的变量
struts2-core-2.1.8.1.jar包中 /org/apache/struts2/default.properties文件中的常量
此文件中的常量可以在struts.xml配置文件中的struts根标签下用<constant name="属性名" value="值"></constant>的方式修改此defautl.properties配置文件中的默认值
default.properties文件中的常量:
struts.i18n.encoding 默认值:UTF-8 相当于request.setCharacterEncoding(“UTF-8”);所以在此处设置后就不需要在servlet中设置request请求对象的编码格式了
struts.action.extension 默认值:action,, 资源地址的后缀名,当访问此资源时必须加上此处配置的后缀名称才能被struts2过滤,逗号中间的为一个缺省值,表示匹配所有的资源
struts.serve.static.browserCache 默认值:true 静态网页是否缓存(默认为要缓存),生产环境下最好为true,开发阶段最好false
struts.configuration.xml.reload 默认值:false 当修改struts的配置文件(struts.xml)后,此应用是否重新部署,默认为不重新部署,生产环境下最好使用false(不重新部署),开发环境下最好为true(重新部署)
struts.devMode 默认值:false 开发模式下设置为true,这样可以打印出更详细的错误信息,具有struts.configuration.xml.reload一样的功能,生产模式下使用false
struts.ui.theme 默认值:xhtml 其他值:simple(表示没有模板)、xhtml(表示自动生成模板)等,设置默认的视图(标签时需要用到(form表单标签))
struts.objectFactory 默认值:spring 与spring框架集成时,指定由spring负责action(动作处理的Bean)对象的创建
struts.enable.DynamicMethodInvocation 默认值:true 该属性是设置struts2是否支持动态调用,该属性的默认值是true.如果需要关闭动态方法调用,则可设置该属性为false(建议关闭,设置为)
struts.multipart.maxSize 默认值:2097152 设置单次上传文件的大小(不是设置单个文件,是单次上传的总大小),默认值为2M
struts2的处理流程
动作类的生命周期:当用户请求对应的资源时,如果有struts2过滤,那么都会创建出一个新动作类,并在处理完动作后自动销毁(所以这个动作类是一个线程安全的)
struts2配置文件的拆分与引用
当程序越来越复杂时,那么把所有包都写在一个配置(struts.xml)文件中容易混淆,不好分辨,也降低了可阅读性。 那么struts2中的配置文件是可以拆分的,并且struts.xml配置文件中提供了一个include标签,用于导入外部的struts.xml配置文件。
示例:
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts><!-- 这是struts2配置文件的根目录 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<!-- 全局范围内的错误页面配置 -->
<package name="scxh1" namespace="/test1" extends="struts-default">
<global-results>
<result name="error">/error.jsp</result>
</global-results>
</package>
<include file=”temp.xml”></include><!-- file属性中写的是要导入struts配置文件的路径,这里引用了下面temp.xml配置文件中的 package包 -->
</struts>
temp.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts><!-- 这是struts2配置文件的根目录 -->
<!-- 当scxh2继承了scxh1这个包时,那么scxh2就有了scxh1的功能了 -->
<package name="scxh2" namespace="/test2" extends="scxh1">
<action name="a1" class="cn.domain.Test" method="exe">
<result name="success">/1.jsp</result>
</action>
</package>
</struts>
struts2中的动态方法调用
也就是说,当我们的动作类中有两个动作方法:execute(),other(),但在struts文件中配置了此动作方法为execute()方法,如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts><!-- 这是struts2配置文件的根目录 -->
<!-- 当scxh2继承了scxh1这个包时,那么scxh2就有了scxh1的功能了 -->
<package name="scxh2" namespace="/test" extends="struts-default">
<action name="helloworld" class="cn.domain.HelloWorldAction" method="execute">
<result name="success">/1.jsp</result>
</action>
</package>
</struts>
这里通过http://localhost:8080/struts/test/helloworld 这个地址访问时,对应的操作方法是execute(),但这里我想访问other()这个操作方法,可以通过url来控制,如:
访问 http://localhost:8080/struts/test/helloworld!other ,那么操作方法将不在调用exectue(),而是调用other()这个操作方法。这样就实现了动态方法调用
备注:在实际开发中,不建议使用动态方法调用。但struts2是默认开启的,关闭动态调用的方法,只需要在struts.xml配置文件中的struts根标签下加上以下标签即可:
<constant name=" struts.enable.DynamicMethodInvocation" value="false"></constant>
struts2中的通配符
在struts.xml配置文件中,action元素的name属性,如有用*号通配符,那么在此method属性和result元素的内容中就可以用{引用第几个*}的方式来引用此name属性中*号位置的值
示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts><!-- 这是struts2配置文件的根目录 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<package name="scxh" namespace="/test1" extends="struts-default">
<action name="user_*" class="cn.domain.User" method="{1}"><!-- 这里的{1} 表示引用action元素中name属性的第一个*通配符
如果访问的url为:http://localhost:8080/struts2.3/test1/user_update
那么这里的{1}就表示update, 调用的方法就是cn.domain.User类中的update操作方法
下面跳转的页面,和这里一样,跳转的页面也将会是/update.jsp-->
<result name="success">/{1}.jsp</result><!--其实这里也可以用EL表达式取出动作类中属性的值,如:<result name="success">/${username}.jsp</result>-->
</action>
</package>
</struts>
struts2中接收表单的参数
表单值的封装:
在struts2中接收表单的参数很简单,只要提交给一个动作,然后struts2会自动的将表单提交过来的数据封装到对应的动作类中(前提是表单中的名称必须和Bean对象中的属性相对应,并且一致)
如果在bean对象中引用了一个对象,如果要给此对象赋值,那么在表单中的name属性的值必须为: 这个对象的变量名.这个对象的属性名
如果是一个多选按钮,那么在bean对象中必须用字符串数组来接收
在提交的表单中,struts2提供了8大基本类型自动转换,如果有其他类型,那么需要创建自定义类型转换器(在struts2中有一个默认的日期类型转换器,但只能转:yyyy-MM-dd这种字符串的日期 )
编码问题:
如果请求方式是post,那么就不需要解决中文乱码问题了,因为struts2的default.properties文件中的struts.i18n.encoding属性已经设置为了UTF-8,所以就不需要设置编码问题了。
如果请求方式个get,那么就需要自己解决中文乱码问题,解决方式是:写一个Filter全局过滤器(注意此过滤器在web.xml中必须配置在struts2过滤器的前面),然后重写request对象中的 getParameter(String)方法,在此方法中判断请求的方式如果为get,如果是就用String value = new String(super.getParameter(name).getByts(“iso-8859-1”),”utf-8”);的方式将其值进行转换(在JSP中已经写过get请求方式乱码转换的全局过滤器)
示例:(将表单中的数据封装到一个javaBean对象中,及这个对象的地址对象中)
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<!-- 通过struts2封装表单中的值 -->
<struts>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- struts2的配置文件不变,struts2会自动将表单中的值赋值给cn.domain.UserAction对象 向对应的属性 -->
<package name="scxh" namespace="/test" extends="struts-default">
<action name="registerUserAction" class="cn.domain.UserAction" method="showUser">
<result name="success">/showUser.jsp</result>
</action>
</package>
</struts>
地址Bean对象
package cn.domain;
import java.io.Serializable;
//这个类是一个地址类
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
private String province;//省份
private String city;//城市
private String detailed;//详细地址
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDetailed() {
return detailed;
}
public void setDetailed(String detailed) {
this.detailed = detailed;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city
+ ", detailed=" + detailed + "]";
}
}
用户Bean对象
package cn.domain;
import java.io.Serializable;
import java.util.Arrays;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//这是一个用户的操作javaBean类
public class UserAction extends ActionSupport implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;//ID
private String name;//姓名
private String gender;//性别
private Integer age;//年龄
private String[] hobby;//爱好(这里是一个数组,对应了多选按钮)
private Address address;//住址(引用了住址对象),对应的表单名称,应该是 address.属性名 的方式 来给此对象中的属性赋值
//这是一个操作的方法,此法是以字符串的形式 存储到 ActionContext(上下文对象)中
public String showUser(){
try{
//获取到上下文对象
ActionContext ac = ActionContext.getContext();
//将当前的user对象转换为字符串
String stringUser = "ID:"+this.id+",name:"+this.name+",gender:"+this.gender+",age:"
+this.age+",hobby:"+Arrays.toString(hobby)+",address:"+address.toString();
//存储到上下文对象中
ac.put("stringUser", stringUser);
return Action.SUCCESS;
}catch(Exception e){
return Action.ERROR;
}
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
表单提交页面(registerUser.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
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 'registerUser.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">
-->
<script type="text/javascript" src="jQueryLib/jquery-1.9.1.min.js"></script>
</head>
<body>
<form action="${pageContext.request.contextPath }/test/registerUserAction" method="post">
<!-- 注意表单的名称要和bean对象的属性一致 -->
ID:<input type="text" name="id"/><br/>
姓名:<input type="text" name="name"/><br/>
性别:<input type="text" name="gender"/><br/>
年龄:<input type="text" name="age"/><br/>
<!-- 当是一个多选按钮时,那么对应的bean的属性 必须是一个字符串数组 -->
爱好:<input type="checkbox" name="hobby" value="上网"/>上网
<input type="checkbox" name="hobby" value="睡觉"/>睡觉
<input type="checkbox" name="hobby" value="看电影"/>看电影<br/>
<!-- 如果bean对象中引用了一个对象,那么可以通过这个对象在bean对象中的 名称.这个对象的属性 这种方式来给这个对象赋值 -->
地址:<br/>
省份:<select name="address.province" id="province">
<option value="你未选择省份">--请选择--</option>
<option value="四川">四川</option>
<option value="广东">广东</option>
</select>
城市:<select name="address.city" id="city">
<option value="你未选择城市">--请选择--</option>
</select>
详细地址:<input type="text" name="address.detailed"/>
<br/>
<input type="submit" value="提交"/>
</form>
</body>
<script type="text/javascript">
$().ready(function(){
$("#province").unbind("change");
$("#province").bind("change",function(){
$("#city").children("option").filter(":gt(0)").remove();
if($(this).val() == "四川"){
var $optionElement1 = $("<option></option>");
$optionElement1.val("南充");
$optionElement1.append("南充");
$("#city").append($optionElement1);
var $optionElement2 = $("<option></option>");
$optionElement2.val("达州");
$optionElement2.append("达州");
$("#city").append($optionElement2);
var $optionElement3 = $("<option></option>");
$optionElement3.val("广安");
$optionElement3.append("广安");
$("#city").append($optionElement3);
}else{
var $optionElement1 = $("<option></option>");
$optionElement1.val("深圳");
$optionElement1.append("深圳");
$("#city").append($optionElement1);
var $optionElement2 = $("<option></option>");
$optionElement2.val("汕头");
$optionElement2.append("汕头");
$("#city").append($optionElement2);
}
});
});
</script>
</html>
结果显示页面(showUser.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
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 'showUser.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>
你提交的用户信息为:${stringUser }
</body>
</html>
结果:
如果在registerUser.jsp页面提交的为:
那么当提交后,showUser.jsp页面显示的结果为:
自定义类型转换器
已有的类型转换器
在struts2如果表单提交的为基本类型,都会进行自动转换,但如果是其他类型,就需要自定义类型转换器
在struts2中有一个默认的日期类型转换器,但输入的格式必须是:yyyy-MM-dd HH:mm:ss.SSS 的方式才能自动转换,但如果是其他的日期格式如:yyyy/MM/dd 那么就需要我们去自定义类型转换器了
类型转换器的注册
自定义类型转换器分为两类。
第一类是:局部自定义类型转换器,只作用于当前的动作类对象的某一个属性。
在动作类的同包中,创建一个名称为:“动作类的名称-conversion.properties”的配置文件
在此配置文件中添加一个映射关系:动作类中要转换的属性名=自定义类型转换器类的完整名称
第二类是:全局范围内的自定义类型转换器,作用于整个被struts2过滤的动作类。
具体做法:在WEB-INF/classes目录下,建立一个名称为"xwork-conversion.properties"的配置文件,
文件中增加以下内容:目标类型全名=验证器的类全名
在WEB-INF/classes/目录下(开发环境对应的目录为src下)建立一个名称为“xwork-conversion.properties”的配置文件
在此配置文件中添加一个映射关系:要转换的类型的完整类名=自定义类型转换器类的完整名称
自定义转换器应存放的包及命名规范
自定义类型转换器的类应放在convertor包中,类名应以Convertor结尾
自定义转换器应继承的类
自定义转换器的类应继承com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter这个类,
并重写父类的 public Object convertValue(Map<String, Object> context, Object value, Class toType)这个方法
类型转换器的的异常处理机制
当类型转换出错时,动作类返回的是“input”,所以在struts.xml配置文件中的这个action标签下 应定义一个input的结果处理器
示例:(定义一个可以转换yyyy/MM/dd这种日期格式的类型转换器)
自定义类型转换器
package cn.convertor;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;
//自定义的类型转换器
public class DateConvertor extends DefaultTypeConverter {
@SuppressWarnings("all")
@Override
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
/*参数解释:
context上下文对象
value用户输入的值(是一个String[]数组)
toTypebean对象中对应属性的set方法所接收的参数的类型(目标类型)
*/
if(value == null){
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
//判断如果目标类型为Date,那么就说明是保存
if(toType == Date.class){
//2013/05/31----->java.util.Date 保存数据时
String strValue = ((String[])value)[0];
try {
Date date = sdf.parse(strValue);
return date;
} catch (ParseException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}else{//否则那么就说明是获取
//java.util.Date----->2013/05/31 获取数据时
Date date = (Date)value;
String strDate = sdf.format(date);
return strDate;
}
}
}
bean对象
package cn.domian;
import java.io.Serializable;
import java.util.Date;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//这个一个用户类(动作类),只有一个生日的属性
public class UserAction extends ActionSupport implements Serializable{
private static final long serialVersionUID = 1L;
private Date birthday;//生日
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String showUser(){
try{
ActionContext ac = ActionContext.getContext();//上下文对象(不是servlet中的上下文对象)
ac.put("birthday", birthday);
return Action.SUCCESS;
}catch(Exception e){
return Action.ERROR;
}
}
}
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<!-- 通过struts2封装表单中的值(自定义类型转换器) -->
<struts>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<package name="scxh" namespace="/test" extends="struts-default">
<action name="addUser" class="cn.domian.UserAction" method="showUser">
<result name="success">/ok.jsp</result>
<result name="input">/input.jsp</result>
</action>
</package>
</struts>
提交的jsp
<form action="${pageContext.request.contextPath }/test/addUser" method="post">
生日:<input type="text" name="birthday"/>
<input type="submit" value="提交"/>
</form>
ok.jsp
<body>
类型转换OK:${birthday }
</body>
input.jsp
<body>
类型转换异常
</body>
全局转换器和局部转换器只是注册时不同:
自定义转换器的注册(局部)
在和动作类同包中(cn.domian)建立一个UserAction-conversion.properties配置文件
内容为:birthday=cn.convertor.DateConvertor
自定义转换器的注册(全局)
开发环境下 在src(部署成功后是在WEB-INF/classes/)目录下创建一个xwork-conversion.properties配置文件
java.util.Date=cn.convertor.DateConvertor
通过struts2框架来获取servlet常用的对象
ActionContext对象
ActionContext对象是动作类的上下文对象
struts2会根据每次的HTTP请求来创建一个ActionContext对象,当请求结束后自动销毁
ActionContext对象是存放在ThreadLocal对象中(ThreadLocal是线程范围内的域对象)的。
通过当前线程作为key获取到的此ActionContext对象(可以保证当前线程中此对象的唯一性,及线程安全性)
ActionContext对象中封装了HttpSession、ServletContext、HttpServletRequest这三个域对象中底层维护的Map集合
获取HttpSession、ServletContext、HttpServletRequest这三个域对象中底层维护的Map集合
在struts2中的动作类中可以获取到ActionContext对象通过此对象来获取以上三个域中维护的Map集合,既然能够获取到这些域中的Map集合,那么向这些Map集合中存值,就相当于向各个域中存入值
示例:
package cn.domain;
import java.io.Serializable;
import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//通过此动作类来获取HttpSession、ServletContext、HttpServletRequest这三个域对象中底层维护的Map集合
public class ScopeAction extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
public String scope(){
/*
* ActionContext对象是动作类的上下文对象(应用与所有的动作类范围)
* struts2会根据每次的HTTP请求来创建一个ActionContext对象,当请求结束后自动销毁
*
* ActionContext对象是存放在ThreadLocal对象中(ThreadLocal是线程范围内的域对象)的。
* 通过当前线程作为key获取到的此ActionContext对象(可以保证当前线程中此对象的唯一性,及线程安全性)
*/
ActionContext ac = ActionContext.getContext();
//获取的是ServletContext对象底层维护的Map集合
Map<String, Object> applicationMap = ac.getApplication();
//向此Map集合中存,值相当于 request.getServletContext().setAttribute("scopeValue","applicationScope");
//此Map集合比较特殊,虽然从request域中可以获取到此Map集合中的值,但此集合中获取不到request域中Map集合的值(OGNL表达式中会详细讲此ActionContext对象及底层维护的集合)
applicationMap.put("scopeValue", "applicationScope");
//获取的是HttpSession对象底层维护的Map集合
Map<String, Object> sessionMap = ac.getSession();
//向此Map集合中存值,相对于request.getSession().setAttribute("scopeValue","sessionScope");
sessionMap.put("scopeValue", "sessionScope");
//向ActionContext中的Map集合中存值,相当于request.setAttribute("scopeValue","requestScope");
ac.put("scopeValue", "requestScope");
return Action.SUCCESS;
}
}
获取servlet中的九大隐式对象
在struts2框架中有两种方式获取servlet中的对象
第一种:通过ServletActionContext类中的静态方法来获取(推荐使用)
package cn.domain;
import java.io.Serializable;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.PageContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//通过ServletActionContext类的静态方法来获取到servlet中的9大隐式对象
public class ServletObj extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
//ServletActionContext此类中可以获取pageContext对象,那么获取了此对象,也就可以获取其他的8个隐式对象了
PageContext pageContext = ServletActionContext.getPageContext();
//还可以获取servletContext对象
ServletContext servletContext = ServletActionContext.getServletContext();
//还可以获取HttpServletRerequest请求对象
HttpServletRequest request = ServletActionContext.getRequest();
//而request对象又可以获取到session对象
HttpSession session = request.getSession();
//还可以获取HttpServletResponse响应对象
HttpServletResponse response = ServletActionContext.getResponse();
//还可以获取到ActionContext动作上下文对象
ActionContext ac = ServletActionContext.getContext();
return Action.SUCCESS;
}
}
第二种:通过实现ServletRequestAware, ServletResponseAware, ServletContextAware这些接口来实现
package cn.domain;
import java.io.Serializable;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.util.ServletContextAware;
import com.opensymphony.xwork2.ActionSupport;
//通过实现ServletRequestAware, ServletResponseAware, ServletContextAware这些接口来实现
public class ServletObj2 extends ActionSupport implements Serializable, ServletRequestAware, ServletResponseAware, ServletContextAware{
private static final long serialVersionUID = 1L;
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext context;
//当实例化此类时,会将request、response、context对象通过以下方法传入
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
@Override
public void setServletContext(ServletContext context) {
this.context = context;
}
}
struts2中的文件上传
在struts2框架中的文件上传很简单,只需要在bean对象中定义一个File类型的属性即可,但其属性名必须和表单中name属性中的值一致
如果要获取文件的名称,就需要在bean对象中定义一个名称为:“文件属性名+FileName”的属性,那么struts2框架会自动将此文件的名称传入,并且解决了中文乱码问题
如果要获取此文件的MIME类型,和获取文件名称一样,在bean对象中定义一个名称为:”文件属性名+ContentType”的属性,那么struts2框架会自动将此文件的MIME类型传入(如:jpg的MIME类型为image/pjpeg)
如果文件超出了总文件的大小,那么动作类会返回一个”input”的字符串,也可以在struts.xml文件中的package标签下用:
<constant name=" struts.multipart.maxSize" value="设置单次上传文件总大小,单位为字节"></constant>标签的形式设置此值,此属性的默认值为:2097152(2M)
示例:单个文件上传
package cn.domain;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//单个文件上传,的操作类
public class SingleImageUpload extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
private String username;//在struts2中如果要获取文本域中的内容,直接定义属性即可(struts2会通过此属性的set方法将此值传入)
private File image;
private String imageFileName;//得到文件的名称(以解决了中文乱码问题),后面的"FileName"是固定写法,不能更改。这里“imageFileName”表示得到image文件的名称
private String imageContentType;//得到文件的MIME类型,后面的"ContentType"是固定写法,不能更改,这里“imageContentType”表示得到image文件的MIME类型
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public File getImage() {
return image;
}
public void setImage(File image) {
this.image = image;
}
public String getImageFileName() {
return imageFileName;
}
public void setImageFileName(String imageFileName) {
this.imageFileName = imageFileName;
}
public String getImageContentType() {
return imageContentType;
}
public void setImageContentType(String imageContentType) {
this.imageContentType = imageContentType;
}
@Override
public String execute() throws Exception {
ActionContext ac = ActionContext.getContext();
//判断这个文件的MIME类型是否是一张图片
if(!this.imageContentType.startsWith("image")){
//向request域中存入值
ac.put("message", "你的用户名为:"+this.username+"<br/>你上传的不是图片");
return Action.SUCCESS;
}
//得到文件存储的绝对路径
String fileStoragePath = ServletActionContext.getServletContext().getRealPath("/files");
//通过这个文件的绝对路径创建一个输出流,并加上此文件的名称
OutputStream out = new FileOutputStream(fileStoragePath+"\\"+imageFileName);
//通过这个image(File对象)创建一个输入流
InputStream in = new FileInputStream(image);
//将输入流的数据,存放到输出流中,这样就完成了文件的上传了
byte[] b = new byte[1024];
int len = -1;
while((len = in.read(b)) != -1){
out.write(b, 0, len);
}
out.flush();
out.close();
in.close();
//向request域中存入值
ac.put("message", "你的用户名为:"+this.username+"<br/>上传图片成功");
return Action.SUCCESS;
}
}
示例:多个文件上传
jsp中的代码
<body>
<h1 align="center">多个文件上传</h1>
<form action="${pageContext.request.contextPath }/multiImage/upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br/>
<!-- 方式一:name属性的命名都一致,那么在对应的Bean对象中,应该有一个File类型名称为image的数组对象 -->
图片1:<input type="file" name="images"><br/>
图片2:<input type="file" name="images"><br/>
<!-- 方式二:name属性的命名不一致,那么在对象的Bean对象中,就应该定义与表单对应个数的属性 并且属性名必须和此表单的name属性保持一致的File对象 -->
文件1:<input type="file" name="file"><br/>
<input type="submit" value="提交">
</form>
</body>
Bean对象中的代码
package cn.domain;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//多个文件上传的操作类
/*
多个文件上传有两种方式,
方式一:
多个file表单域的name属性都一致,那么在Bean对象中就可以用这个表单的name属性的值,定义一个File的数组对象
方式二:
多个file表单域的name属性不一致,那么在Bean对象中就应该定义与表单个数一致的File类型的成员变量,并且其成员变量名必须和表单中name属性的值相对应
*/
public class MultiImageUpload extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private File[] images;//获取多个图片
private String[] imagesFileName;//获取这些图片的名称
private String[] imagesContentType;//获取这些图片的MIME类型
private File file;//获取某一个文件
private String fileFileName;//获取这个文件的名称
private String fileContentType;//获取这个文件的MIME类型
@Override
public String execute() throws Exception {
ActionContext ac = ActionContext.getContext();
//得到文件存储的绝对路径
String fileStoragePath = ServletActionContext.getServletContext().getRealPath("/files");
String message = "你的用户名为:"+this.username;
//获取多个图片
if(images != null && images.length>0){
System.out.println("------------------------------------------------");
for(int i=0; i<images.length; i++){
if(!this.imagesContentType[i].startsWith("image")){
message += "<br/>你上传的第"+(i+1)+"个文件不是图片!";
continue;
}
OutputStream out = new FileOutputStream(fileStoragePath+"\\"+imagesFileName[i]);
InputStream in = new FileInputStream(images[i]);
byte[] b = new byte[1024];
int len = -1;
while((len=in.read(b)) != -1){
out.write(b, 0, len);
}
out.flush();
out.close();
in.close();
message += "<br/>第"+(i+1)+"张图片上传成功!";
}
}
//获取这个文件
if(file != null){
OutputStream out = new FileOutputStream(fileStoragePath+"\\"+fileFileName);
InputStream in = new FileInputStream(file);
byte[] b = new byte[1024];
int len = -1;
while((len=in.read(b)) != -1){
out.write(b, 0, len);
}
out.flush();
out.close();
in.close();
message += "<br/>第最后一个文件上传成功";
}
ac.put("message", message);
return Action.SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public File[] getImages() {
return images;
}
public void setImages(File[] images) {
this.images = images;
}
public String[] getImagesFileName() {
return imagesFileName;
}
public void setImagesFileName(String[] imagesFileName) {
this.imagesFileName = imagesFileName;
}
public String[] getImagesContentType() {
return imagesContentType;
}
public void setImagesContentType(String[] imagesContentType) {
this.imagesContentType = imagesContentType;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFileFileName() {
return fileFileName;
}
public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
}
public String getFileContentType() {
return fileContentType;
}
public void setFileContentType(String fileContentType) {
this.fileContentType = fileContentType;
}
}
struts2中的文件下载(或参考:struts2.85_fileload工程或参考 L07 Struts2文件上传下载.pdf 文档)
在struts2中的文件下载稍微要复杂一点
1、 应在动作类中的准备好(用成员变量定义,并生成set和get方法):
flileType 文件的MIME类型,在文件上传时刻可以获取到
fileLength 文件的大小
fileName 文件的名称(下载时用,如果只是在jsp显示图片,可以不用定义)
InputStream 文件的输入流(名称可以自定义,但在向结果处理器类注入此名称时 要一致)
2、 在动作类中把以上属性的值获取到
3、 在strusts.xml配置文件中配置stream的结果处理器
模板:
<package name="load" namespace="/load" extends="struts-default">
<action name="loadFile" class="cn.domain.UserAction" method="loadFile">
<result name="downloadOK" type="stream"><!--以下都是向此stream结果处理器对应的类中注入值,此结果处理器的注册在struts2-core.jar包中的struts-default.xml文件中-->
<param name="contentType">${fileType}</param><!-- 注入文件的类型(重动作类中的成员变量中引用的) -->
<param name="contentLength">${fileLength}</param><!-- 注入文件的大小(重动作类中的成员变量中引用的) -->
<param name="inputName">imageInputStream</param><!-- 注入动作类中此文件输入流的名称(数据源输入流) -->
<param name="contentDisposition">attachment;filename="${fileName}"</param><!-- 设置contentDisposition响应头的值,如果不下载只是在jsp页面显示此文件,可以不用注入此信息 -->
</result>
</action>
</package>
示例:(文件的下载,与图片的显示)
JSP页面:
<%@ 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 'index.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">
</head>
<body>
<!-- 显示图片,访问是用:http://localhost:8080/struts2.85_fileload/load/showAllFileName url访问 -->
系统中的图片有:<br/>
<!-- 遍历这个allFileName这个list集合 -->
<s:iterator value="%{#allFileName}" var="fileName">
<s:property value="#fileName"/><!-- 显示文件名称 -->
<!-- 重写此url用于只是显示此图片 -->
<s:url namespace="/load" action="showImage" var="showUrl">
<!-- 用当前的文件名当做参数 -->
<s:param name="fileName" value="%{#fileName}"></s:param>
</s:url><!-- 获取显示的url -->
<img alt="${fileName }" src="${showUrl}" width="70px" height="50px"/>
<!-- 重写此url用于下载此文件 -->
<s:url namespace="/load" action="download" var="downlodUrl">
<!-- 用当前的文件名当做参数 -->
<s:param name="fileName" value="%{#fileName}"></s:param>
</s:url><!-- 获取下载的url -->
<s:a href="%{#downlodUrl}">下载</s:a>
<br/>
</s:iterator>
</body>
</html>
struts.xml配置页面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<!-- struts2文件下载,及直接在浏览器显示图片显示 -->
<struts>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- 访问是用:http://localhost:8080/struts2.85_fileload/load/showAllFileName url访问 -->
<package name="load" namespace="/load" extends="struts-default">
<action name="*" class="cn.domain.UserAction" method="{1}">
<!-- 此结果处理器用于显示文件的名称 -->
<result name="success">/{1}.jsp</result>
<!-- 此结果处理器只是用于下载图片。
从此结果处理器中可以看出,只是比下面显示图片多注入了一个contentDisposition属性的值而已,这其实是HTTP协议的响应头 -->
<result name="downloadOK" type="stream"><!-- 以下都是向此stream结果处理器对应的类中注入值,此结果处理器的注册在struts2-core.jar包中的struts-default.xml文件中 -->
<param name="contentType">${fileType}</param><!-- 注入文件的类型(重动作类中的成员变量中引用的) -->
<param name="contentLength">${fileLength}</param><!-- 注入文件的大小(重动作类中的成员变量中引用的) -->
<param name="inputName">imageInputStream</param><!-- 注入动作类中此文件输入流的名称(数据源流) -->
<param name="contentDisposition">attachment;filename="${fileName}"</param><!-- 设置contentDisposition响应头的值 -->
</result>
<!-- 此结果处理器只是是用于显示图片,返回图片的二进制流 -->
<result name="showImageOK" type="stream"><!-- 以下都是向此stream结果处理器对应的类中注入值,此结果处理器的注册在struts2-core.jar包中的struts-default.xml文件中 -->
<param name="contentType">${fileType}</param><!-- 注入文件的类型(重动作类中的成员变量中引用的) -->
<param name="contentLength">${fileLength}</param><!-- 注入文件的大小(重动作类中的成员变量中引用的) -->
<param name="inputName">imageInputStream</param><!-- 注入动作类中此文件输入流的名称(数据源流) -->
</result>
</action>
</package>
</struts>
动作类:
package cn.domain;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import cn.utils.FileUtils;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
//直接获取此文件的数据(在jsp页面显示),或下载此文件
public class UserAction extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
//定义一个图片的输入流对象(生成get和set方法)
private InputStream imageInputStream;
//此文件的类型MIME的类型,此类型可以再上传时存入数据库,那么在下载时就从数据库中取出来
private String fileType = "image/jpeg";
private String fileName;//记录文件的名称
private Long fileLength;//记录文件的大小
//显示files文件夹中所有的图片
public String showAllFileName() {
//files是文件的存储目录
String filePath = "files";
//获取到files目录的绝对路径
String realPath = ServletActionContext.getServletContext().getRealPath(filePath);
//获取到此绝对路径中的所有子文件(及以下层)的文件名称,并返回一个list集合
List<String> allFileName = FileUtils.getAllFileName(realPath);
//将这个所有文件名称的集合存入action(ActionContext)域中
ActionContext.getContext().put("allFileName", allFileName);
//返回字符串,要调用指定的结果处理器
return Action.SUCCESS;
}
//下载图片
public String download() {
try {
//解决中文乱码问题(不然无法创建输入流)
//因为此参数在showAllFileName.jsp传递过来时,<s:url>标签中的<s:param>参数传递将此中文重写成了ISO-8859-1的编码格式了,
//struts2在检查此编码时发现没有中文,就没有自动将其转换为UTF-8,所以这里需要手动转换(不然乱码的话下面创建输入流时无法创建成功)
fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
String filePath = "files";
//获取到files目录的绝对路径
String realPath = ServletActionContext.getServletContext().getRealPath(filePath);
//根据此绝对路径创建出一个文件输入流并赋值给imageInputStream成员变量,
//对应的结果处理器:<result name="downloadOK" type="stream">
//<param name="inputName">imageInputStream</param>
//会从此成员变量中去取流中的数据
imageInputStream = new FileInputStream(realPath+"\\"+fileName);
//获取文件的大小
File file = new File(realPath+"\\"+fileName);
fileLength = file.length();
//解决中文乱码问题(不然文件下载名将是乱码)
//当创建好输入流后(此fileName用完后)应比此fileName的值再转换成ISO-8859-1,
//因为结果处理器中的:<param name="contentDisposition">attachment;filename="${fileName}"</param>
//参数注入会取出此文件名称(如果不转换成ISO-8859-1下载的文件名将是乱码)
fileName = new String(fileName.getBytes("UTF-8"),"ISO-8859-1");
ServletActionContext.getRequest().setAttribute("fileName", fileName);
//返回字符串,要调用指定的结果处理器
return "downloadOK";
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}
//只是在JSP页面显示图片
public String showImage() {
try {
//解决中文乱码问题(不然无法创建输入流)
//因为此参数在showAllFileName.jsp传递过来时,<s:url>标签中的<s:param>参数传递将此中文重写成了ISO-8859-1的编码格式了,
//struts2在检查此编码时发现没有中文,就没有自动将其转换为UTF-8,所以这里需要手动转换(不然乱码的话下面创建输入流时无法创建成功)
fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
//files是文件的存储目录
String filePath = "files";
//获取到files目录的绝对路径
String realPath = ServletActionContext.getServletContext().getRealPath(filePath);
//根据此绝对路径创建出一个文件输入流并赋值给imageInputStream成员变量,
//对应的结果处理器:<result name="downloadOK" type="stream">
//<param name="inputName">imageInputStream</param>
//会从此成员变量中去取流中的数据
imageInputStream = new FileInputStream(realPath+"\\"+fileName);
//获取文件的大小
File file = new File(realPath+"\\"+fileName);
fileLength = file.length();
//返回字符串,要调用指定的结果处理器
return "showImageOK";
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}
public InputStream getImageInputStream() {
return imageInputStream;
}
public void setImageInputStream(InputStream imageInputStream) {
this.imageInputStream = imageInputStream;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Long getFileLength() {
return fileLength;
}
public void setFileLength(Long fileLength) {
this.fileLength = fileLength;
}
}
自定义拦截器
拦截器的生命周期
当应用部署时被创建,当应用卸载时销毁
自定义拦截器应存放的包及命名规范
自定义拦截器应存放在interceptor的包下,并且类名应以Interceptor结尾
自定义拦截器应继承的类
自定义拦截器应实现com.opensymphony.xwork2.interceptor.Interceptor; 接口
或继承:com.opensymphony.xwork2.interceptor.AbstractInterceptor抽象类,(继承此抽象类就不用写init和destroy方法了)
并重写其public String intercept(ActionInvocation invocation) throws Exception {}方法,这里传入的invocation对象,就是动作类要执行的动作方法(和放行的意识差不多)
重点:拦截器的注册和引用
在struts.xml配置文件的package标签下用以下方式为此拦截器注册
<interceptors><!-- 注册自定义拦截器,并为此拦截器取一个名称 -->
<interceptor name="permissionInterceptor" class="cn.interceptor.PermissionInterceptor"></interceptor>
</interceptors>
引用
引用有两种方式(只是在struts.xml配置文件中体现出了此两种方式)
第一种方式:此拦截器只作用于所有引入了此拦截器的动作
第二种方式:此拦截器只作用于所有继承了此拦截器组的package的 package
示例:(方式一:此拦截器只作用于所有引入了此拦截器的动作)或者参考struts2.9工程
以下示例的案例是,拦截没有登录的用户访问主页
自定义拦截器
package cn.interceptor;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
//自定义的拦截器(拦截没有登录的非法访问)
public class PermissionInterceptor implements Interceptor {
private static final long serialVersionUID = 1L;
//此方法会在执行动作对象中的动作方法前执行此方法,并将动作方法的对象传入
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//判断是否有用户登录,如果没有就直接返回login
if(ServletActionContext.getRequest().getSession().getAttribute("user")==null){
return Action.LOGIN;
}
//这里的invocation对象就是Verify这个Bean类中的execute()方法;
//调用invocation对象的invoke();方法其实就是执行 Verify这个Bean类中的execute()方法(动作类中的动作方法);
//也可以看着是放行,如果有下一个拦截器就执行下一个拦截器,如果没有拦截器了,就直接执行动作方法
String result = invocation.invoke();
return result;
}
//当此拦截器销毁时会调用此方法
@Override
public void destroy() {
System.out.println("PermissionInterceptor拦截器被销毁了");
}
//当此拦截器初始化时会调用此方法
@Override
public void init() {
System.out.println("PermissionInterceptor拦截器被创建了");
}
}
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<!-- 拦截器案例 拦截没有登录的用户访问主页 -->
<!-- 自定义拦截器作用于 引入了此拦截器的动作 -->
<struts>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- 定义默认的结果处理器,并在其中注册一个自己的拦截器 -->
<package name="defaultPackage" abstract="true" extends="struts-default">
<interceptors><!-- 注册自定义拦截器,并为此拦截器取一个名称 -->
<interceptor name="permissionInterceptor" class="cn.interceptor.PermissionInterceptor"></interceptor>
<!-- 此标签中可以注册多个拦截器 -->
</interceptors>
<global-results>
<result name="login" type="redirect">/login.jsp</result>
<result name="error">/WEB-INF/page/error.jsp</result>
</global-results>
</package>
<!-- 继承了defaultPackage包 -->
<package name="login" namespace="/login" extends="defaultPackage">
<action name="action" class="cn.domain.LoginAction" method="execute">
<result name="success" type="redirectAction"><!-- 如果没有导入自定义的过滤器,那么这个动作还是 导入了默认的拦截器(和原来的动作一样) -->
<param name="namespace">/index</param>
<param name="actionName">action</param>
</result>
</action>
</package>
<!-- 继承了defaultPackage包 -->
<package name="index" namespace="/index" extends="defaultPackage">
<action name="action" class="cn.domain.Verify" method="execute">
<interceptor-ref name="defaultStack"></interceptor-ref><!-- 注意此默认的拦截器(或拦截器的其他组)必须导入在自定义拦截器的前面 -->
<!-- 注意在一个动作类中只要导入了一个自定义拦截器,那么哪些默认的拦截器将失效,必须手动导入默认的拦截器(并且默认的拦截器必须放在自定义拦截器的前面) -->
<interceptor-ref name="permissionInterceptor"></interceptor-ref>
<!-- 如果在此处导入此拦截器,那么这个拦截器只作用于此动作 -->
<result name="success">/index.jsp</result>
<result name="login" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
示例:(方式二:此拦截器只作用于所有继承了此拦截器组的package的 package)或者参考struts3.01工程
以下示例的案例是,拦截没有登录的用户访问主页
自定义拦截器
package cn.interceptor;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
//自定义拦截器,判断是否有登录
public class PermissionInterceptor implements Interceptor {
private static final long serialVersionUID = 1L;
//此方法会在执行动作对象中的动作方法前执行此方法,并将动作方法的对象传入
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//判断session域中是否有 用户登录
if(ActionContext.getContext().getSession().get("user") == null){
return Action.LOGIN;
}
//执行动作类中的动作方法,并将其返回值 获取到
String result = invocation.invoke();
//返回动作方法的返回值
return result;
}
//当此拦截器销毁时会调用此方法
@Override
public void destroy() {
System.out.println("PermissionInterceptor拦截器被销毁了");
}
//当此拦截器初始化时会调用此方法
@Override
public void init() {
System.out.println("PermissionInterceptor拦截器被创建了");
}
}
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<!-- 拦截器案例 拦截没有登录的用户访问主页 -->
<!-- 自定义拦截器作,用于所有继承了此拦截器组包的package(推荐使用)
也就是说,此拦截器只作用于所有继承了此拦截器组的package的 package -->
<struts>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- 定义默认的结果处理器 -->
<package name="defaultPackage" abstract="true" extends="struts-default">
<global-results>
<result name="login" type="redirect">/login.jsp</result>
<result name="error">/WEB-INF/page/error.jsp</result>
</global-results>
</package>
<!-- 定义自己的拦截器组包,也就是说,谁继承了我的拦截器组包,那么它将拥有我自定定义的拦截器功能 -->
<package name="myDefaultStackPackage" abstract="true" extends="defaultPackage">
<interceptors>
<!-- 注册自己的拦截器 -->
<interceptor name="permissionInterceptor" class="cn.interceptor.PermissionInterceptor"></interceptor><!-- 此标签中可以注册多个拦截器 -->
<!-- 定义一个我自己的默认的拦截器组,此组中可以引用拦截器也可以引用拦截器的组 -->
<interceptor-stack name="myDefaultStack">
<!-- 此组中引用了默认的拦截器组 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 并添加了我自己的过滤器 -->
<interceptor-ref name="permissionInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myDefaultStack"/><!--重新设置默认的拦截组 -->
</package>
<!-- 处理登录的动作,不能被自定义拦截器拦截,因为拦截器先执行而动作方法后执行,拦截了就无法登录.所以包就不必继承myDefaultStackPackage自定义的拦截器组包了 -->
<package name="login" namespace="/login" extends="defaultPackage">
<action name="action" class="cn.domain.LoginAction" method="execute">
<result name="success" type="redirectAction">
<param name="namespace">/index1</param>
<param name="actionName">action</param>
</result>
</action>
</package>
<!-- 主页1的动作包 ,因为此资源需要登录后访问,那么就需要自定定义拦截器,所以就继承myDefaultStackPackage包-->
<package name="index1" namespace="/index1" extends="myDefaultStackPackage">
<action name="action" class="cn.domain.Index1Action" method="execute">
<result name="success">/WEB-INF/page/index1.jsp</result>
</action>
</package>
<!-- 主页2的动作包 ,因为此资源需要登录后访问,那么就需要自定定义拦截器,所以就继承myDefaultStackPackage包-->
<package name="index2" namespace="/index2" extends="myDefaultStackPackage">
<action name="action" class="cn.domain.Index2Action" method="execute">
<!-- <interceptor-ref name="myDefaultStack"></interceptor-ref>
如果没有设置<default-interceptor-ref name="myDefaultStack"/>默认的拦截组,也可以在此处用以上注释了的方式引用拦截组,
那么此拦截组将只是作用于此动作
-->
<result name="success">/WEB-INF/page/index2.jsp</result>
</action>
</package>
</struts>
struts2中的异常描述Map集合
在struts2中有两种异常描述的Map集合,这两个Map集合中只要有一条或以上的错误映射关系,那么动作类中的动作方法将不会执行
第一种:fieldErrors异常中的Map集合(字段(属性)错误)
此Map集合是放有映射关系(属性名-异常描述)的异常信息,如果类型转换异常,验证异常。如果需要在JSP页面显示此Map集合中的异常描述信息可以通过<s:fielderror></s:fielderror>标签的方法显 示此Map集合中所有的错误信息,也可以通过<s:fielderror fieldName="属性名(也就是key)"></s:fielderror>的方式获取指定属性的异常描述信息。
第二种:actionErrors异常中的Map集合(动作(action)错误)
比如在无字段验证时添加的异常信息就会被添加到此Map集合中。如果要在JSP页面显示此Map集合中的异常描述信息,只能通过<s:actionerror/>标签的方式显示此Map中所有的异常描述信息
struts2中的后台表单验证
在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验,因为我们在填充模型(Bean对象)时,不是每次填充模型都需要验证。比如我现在需要查询(对应find()动作方法),就只填充一个ID属性时,那么将不会执行find()动作方法,因为验证不通过,所以就需要对指定动作方法进行校验。
对于输入校验struts2提供了两种实现方法
1、 采用手工编写代码实现
对所有动作方法进行校验
一、 动作类需要继承com.opensymphony.xwork2.ActionSupport;这个类
二、 重写其中的public void validate() {}方法,并在其中写验证的代码
三、 如果此属性没有通过验证,那么就将此属性名及错误描述以映射的关系添加到super.addFieldError("username", "用户名不能为空");此方法中,此方法底层维护的是一个Map集合,只要此集合中有一条或以上的映射关系,那么验证将不会通过,并会返回”input”字符串,所以要配置”input”的结果处理器(result)。此方法在动作方法执行之前执行,如果验证没有通过那么动作方法将不会再执行.
四、如果要在前台显示此错误描述,需导入struts2的标签库<%@taglib uri="/struts-tags" prefix="s" %>然后再用<s:fielderror fieldName="username"></s:fielderror>标签进行显示,因为此错误描述的Map集合是存储在request域中的,所以只有通过转发,此标签才会提取到Map集合中的错误描述信息
示例:(表单的验证对所有动作方法进行验证)
动作类
package cn.domain;
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class User extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
private String username;//验证:用户名只能是 数字、字母、下划线,并且不能以数字开头,长度为:2~6
private String password;//验证:密码只能是 数字、字母,长度为:6~10
private String email;//验证:正确格式的邮箱
//此方法会验证所有的动作方法
//此方法用于验证(此方法会在动作方法前面被调用,如果addFieldError()方法底层维护的Map集合中有一条或以上的记录,那么验证将不会通过。
//并会返回一个"input"字符,所以需要定义一个"input"的结果处理器。如果验证不通过那么动作方法将不会被调用(这样保证了数据的正确性))
@Override
public void validate() {
//验证用户名
if(this.username==null || "".equals(this.username.trim())){
super.addFieldError("username", "用户名不能为空");
}else if(!this.username.matches("^[a-zA-Z]\\w{2,6}$")){
super.addFieldError("username", "用户名只能是 数字、字母、下划线,并且不能以数字开头,长度为:2~6");
}
//验证密码
if(this.password==null || "".equals(this.password.trim())){
super.addFieldError("password", "密码不能为空");
}else if(!this.password.matches("^[0-9a-zA-Z]{6,10}$")){
super.addFieldError("password", "密码只能是 数字、字母,长度为:6~10");
}
//验证邮箱
if(this.email==null || "".equals(this.email.trim())){
super.addFieldError("email", "邮箱不能为空");
}else if(!this.email.matches("^[0-9a-zA-Z_]+@[0-9a-zA-Z]+((\\.com)||(\\.cn)){1,3}$")){
super.addFieldError("email", "请输入一个格式正确的邮箱");
}
}
//如果以上方法验证失败(就是super.addFieldError();方法底层维护的Map集合中有一条或以上的映射关系),那么此操作方法将不会执行
public String register(){
System.out.println("执行了动作方法register()");
ActionContext.getContext().put("message", "恭喜你,你已成功注册");
return Action.SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
<!-- 采用手工编写代码的方式实现struts2的表单后台验证 -->
<!-- 案例:用户的注册 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- 默认的结果处理器 -->
<package name="defaultResult" abstract="true" extends="struts-default">
<global-results>
<result name="input">/login.jsp</result>
</global-results>
</package>
<package name="registerUser" namespace="/registerUser" extends="defaultResult">
<action name="action" class="cn.domain.User" method="register">
<result name="success">/WEB-INF/success.jsp</result>
</action>
</package>
</struts>
注册页面及错误信息显示
<!-- 导入struts2的标签库 -->
<%@taglib uri="/struts-tags" prefix="s" %>
<body>
<p>struts2之表单后台验证</p>
<h1 align="center">用户注册</h1>
<form action="${pageContext.request.contextPath }/registerUser/action" method="post">
用户名:<input type="text" name="username"/><s:fielderror fieldName="username"></s:fielderror><br/><!--此标签用于显示错误描述-->
密码:<input type="password" name="password"/><s:fielderror fieldName="password"></s:fielderror><br/>
邮箱:<input type="text" name="email"/><s:fielderror fieldName="email"></s:fielderror><br/>
<input type="submit" value="注册"/>
</form>
</body>
对指定的动作方法进行校验(推荐使用)
此步骤和上面(对所有动作方法进行校验)的步骤一样,但第二步不同,不能重写public void validate() {}方法了。需要自定义一个public void validateXxx() {}验证方法,此处的Xxx代表动作方法名称,但此处动作方法名称应首字母大写,如: public void validateAdd() {}, 那么public String add() {}动作将会被验证
示例:(指定的动作方法进行校验)
动作类
package cn.domain;
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class User extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
private String username;//验证:用户名只能是 数字、字母、下划线,并且不能以数字开头,长度为:2~6
private String password;//验证:密码只能是 数字、字母,长度为:6~10
private String email;//验证:正确格式的邮箱
//对指定动作方法进行校验(如果用了此种方式进行校验,那么 “对所有动作方法进行校验” 的验证方法就不能写了,否则此种方式会无效)
//此验证方法,只会验证register()这个动作方法
//(如果需要验证其他动作方法,就重新再定义一个验证方法validateXxx,这个Xxx表示动作方法名称,但此处的动作方法名称首字母大写)
public void validateRegister(){
//验证用户名
if(this.username==null || "".equals(this.username.trim())){
super.addFieldError("username", "用户名不能为空");
}else if(!this.username.matches("^[a-zA-Z]\\w{2,6}$")){
super.addFieldError("username", "用户名只能是 数字、字母、下划线,并且不能以数字开头,长度为:2~6");
}
//验证密码
if(this.password==null || "".equals(this.password.trim())){
super.addFieldError("password", "密码不能为空");
}else if(!this.password.matches("^[0-9a-zA-Z]{6,10}$")){
super.addFieldError("password", "密码只能是 数字、字母,长度为:6~10");
}
//验证邮箱
if(this.email==null || "".equals(this.email.trim())){
super.addFieldError("email", "邮箱不能为空");
}else if(!this.email.matches("^[0-9a-zA-Z_]+@[0-9a-zA-Z]+((\\.com)||(\\.cn)){1,3}$")){
super.addFieldError("email", "请输入一个格式正确的邮箱");
}
}
//如果以上方法验证失败(就是super.addFieldError();方法底层维护的Map集合中有一条或以上的映射关系),那么此操作方法将不会执行
public String register(){
System.out.println("执行了动作方法register()");
ActionContext.getContext().put("message", "恭喜你,你已成功注册");
return Action.SUCCESS;
}
//如果用的是 validateRegister()方法验证,那么此动作方法不会被验证(就是只要执行就会通过)
public String find(){
System.out.println("执行了动作方法find()");
ActionContext.getContext().put("message", "你只填充了username属性给User模型(Bean对象)但没有被验证,因为find()动作不会被验证");
return Action.SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
jsp代码
<body>
<p>struts2之表单后台验证</p>
<h1 align="center">用户注册</h1>
<form action="${pageContext.request.contextPath }/findUser/action" method="post">
用户名:<input type="text" name="username"/><br/><!--只填充了username属性-->
<input type="submit" value="查询"/>
</form>
</body>
输入校验的流程
1. 类型转换器对请求参数执行类型转换,并把转换后的值赋值给action(Bean对象)的属性中,如果在执行类型转换的过程中出现了异常,系统会将异常信息保存到ActionContext对象中,conversionError拦截器会将类型转换的异常信息方法到fieldErrors中的Map集合里,不管类型转换是否出现异常,都会进入第2步。
2. 系统通过反射技术(就是内省),先调用action(动作类)中与动作方法名对应的validateXxx方法,Xxx为动作方法名,如果此动作方法没有验证方法(validateXxx),那么将直接通过。
3. 再调用action(动作类)中的validate()方法,如果动作类中没有重写此方法,那么将调用父类中的validate()方法,但父类中的validate()方法中没有方法体(就是无论如何都通过验证)。 此处应注意,如果重写了父类中的validate()方法,那么所有的动作都将会被验证,那么如果写了的validateXxx方法,就相当于没有作用.所以使用了validateXxx方法方法,就没有必须重写父类中的validate()方法方法了。
4. 如果经过上面3步,系统中的fieldErrors中的Map集合中存在错误信息(就是此存放错误信息的Map集合的size大于0),系统自动将请求转发至名称为input的视图,所以需要知道”input”的结果处理器。如果系统中的fieldErrors中的Map集合中没有任何的错误信息,系统将执行action(动作类)中的动作方法。
5. 需注意,因为类型转换错误,或验证失败都会转发至input的视图,所以此处需注意 到底是类型转换错误,还是验证失败了
2、 基于XML配置方式实现
1、 使用XML配置的方式实现输入校验时,Action(动作类)也需要继承ActionSupport这个类,并且提供校验配置文件.校验配置文件和action(动作类)放在同一个包下。但“对所有的动作方法进行校验”与“对指定的动作方法进行校验”的配件文件的命名规则不同,下面会讲。
2、 关联此配置文件的dtd约束文件(此dtd文件可以到xwork-core-x.x.x.jar包中的根目录下去拷贝xwork-validator-1.0.3.dtd 这个dtd约束文件),然后将此文件放在/WEB-INF/dtds目录下,如果没有提示功能,就需要到MyEclipes/Window/Preferences页面去搜索XML Catalog,并点击,然后单击add,Location栏填写dtd约束文件的路径, Key type栏选择URI,Key栏为dtd约束文件的URI,单击OK
3、 如果验证失败也会返回”input”字符串,所以需要配置”input”的结果处理器。
4、 如果要在前台显示此错误描述,需导入struts2的标签库<%@taglib uri="/struts-tags" prefix="s" %>然后再用<s:fielderror fieldName="username"></s:fielderror>标签进行显示,因为此错误描述的Map集合是存储在request域中的,所以只有通过转发,此标签才会提取到Map集合中的错误描述信息
此配置文件的模板
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<!-- field此标签代表一个属性 -->
<field name="username">
<!-- 此标签代表一个验证器,在xwork-core-x.x.x.jar/com.opensymphony.xwork2.validator.validators/default.xml文件中有已经定义好的验证器 -->
<!-- requiredstring验证器验证的是 值不能为null或“”字符串 -->
<field-validator type=" requiredstring">
<param name="trim">true</param><!-- 此处是设置是否去掉 用户输入的值 的前后空格,缺省状态下此值为true -->
<message>用户名不能为空</message><!-- 如果没有通过此验证器的错误描述 -->
</field-validator>
</field>
</validators>
对所有的动作方法进行校验
“对所有的动作方法进行校验”与”对指定的动作方法进行校验”的区别就在于命名规则不同而已
配置文件的命名规范:ActionClassName-validation.xml,其中ActionClassName为动作类的名称, -validation为固定写法,如果动作类名为cn.domain.UserAction,那么该配置文件的命名应为:UserAction-validation.xml。
对指定的动作方法进行校验(推荐使用)
“对所有的动作方法进行校验”与”对指定的动作方法进行校验”的区别就在于命名规则不同而已
配置文件的命名规范:ActionClassName-ActionName-validation.xml,其中ActionClassName为动作类的名称,ActionName为动作名(struts.xml中package标签下action标签中的name属性的值) -validation为固定写法,如果动作类名为cn.domain.UserAction, 动作名为user_add,那么此配置文件的命名应为:UserAction-user_add-validation.xml。
验证器的配置文件示例:
<!-- 此配置文件的作用范围(所有的动作方法,或指定的动作方法):
如果作用与 所有的动作方法
只需要将此文件的名称改为:User-validation.xml即可(动作类名-固定写法)
如果作用与 指定的动作方法(设为registerAction动作名,这里的动作名是:struts.xml中package标签下action标签中的name属性的值)
只需要将此文件的名称改为:User-registerAction-validation.xml即可(动作类名-要验证的动作名-固定写法)
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<!-- field此标签代表一个属性,这里代表username属性 -->
<field name="username">
<!-- 此标签代表一个验证器,在xwork-core-x.x.x.jar/com.opensymphony.xwork2.validator.validators/default.xml文件中有已经定义好的验证器 -->
<!--requiredstring验证器验证的是 值不能为null或“”空字符串 -->
<field-validator type="requiredstring">
<param name="trim">true</param><!-- 此处是设置是否去掉 用户输入的值 的前后空格,缺省状态下此值为true -->
<message>用户名不能为空</message><!-- 如果没有通过此验证器的错误描述
(此描述会以映射关系的形式添加到 系统中的fieldErrors中的Map集合(异常描述信息的集合)中,key是当前的属性名,value是此描述信息) -->
</field-validator>
<!-- regex验证器是自己注入一个正则表达式,然后验证
(注意:为了保证此正则表达式中的特殊字符也以字符形式显示,那么此正则表达式必须写在<![CDATA[ 正则表达式 ]]>之中,此正则表达式不需要转义字符) -->
<field-validator type="regex">
<param name="expression"><![CDATA[^[a-zA-Z]\w{2,6}$]]></param>
<message>用户名只能是 数字、字母、下划线,并且不能以数字开头,长度为:2~6</message><!-- 如果没有通过此正则表达式的错误描述 -->
</field-validator>
</field>
<!-- 这里代表password属性 -->
<field name="password">
<field-validator type="requiredstring">
<param name="trim">true</param><!-- 此处是设置是否去掉 用户输入的值 的前后空格,缺省状态下此值为true -->
<message>密码不能为空</message><!-- 如果没有通过此验证器的错误描述 -->
</field-validator>
<!-- regex验证器是自己注入一个正则表达式,然后验证
(注意:为了保证此正则表达式中的特殊字符也以字符形式显示,那么此正则表达式必须写在<![CDATA[ 正则表达式 ]]>之中,此正则表达式不需要转义字符) -->
<field-validator type="regex">
<param name="expression"><![CDATA[^[0-9a-zA-Z]{6,10}$]]></param>
<message>密码只能是 数字、字母,长度为:6~10</message><!-- 如果没有通过此正则表达式的错误描述 -->
</field-validator>
</field>
<!-- 验证配置文件的 另一种写法,功能和上面都一样 -->
<!-- 验证邮箱email属性 -->
<validator type="requiredstring"><!-- 注册验证器,判断是否为null或“”字符串 -->
<param name="fieldName">email</param><!-- 注入要验证的属性 -->
<param name="trim">true</param><!-- 注入是否去掉属性的值前后的空格 -->
<message>邮箱不能为空!</message><!-- 如果没有通过此验证器的错误描述 -->
</validator>
<!-- regex验证器是自己注入一个正则表达式,然后验证
(注意:为了保证此正则表达式中的特殊字符也以字符形式显示,那么此正则表达式必须写在<![CDATA[ 正则表达式 ]]>之中,此正则表达式不需要转义字符) -->
<validator type="regex"><!-- 在struts验证器中也提供了一个验证邮箱的验证器 -->
<param name="fieldName">email</param>
<param name="expression"><![CDATA[^[0-9a-zA-Z_]+@[0-9a-zA-Z]+((\.com)||(\.cn)){1,3}$]]></param>
<message>请输入一个格式正确的邮箱!,你输入的值为:${email},当前验证的字段为:${fieldName}</message><!-- 如果没有通过此正则表达式的错误描述 -->
<!--通过以上的EL表达式可以看出 这里的EL表达式可以取到这里相关连的动作类中的属性的值,${email}取出的是动作类中email属性的值。${fieldName}取出的是验证器类中fieldName属性的值,这里取出的是“email”-->
</validator>
</validators>
struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
<!-- 采用XML配置文件的方式实现struts2的表单后台验证 -->
<!-- 案例:用户的注册 -->
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!-- 默认的结果处理器 -->
<package name="defaultResult" abstract="true" extends="struts-default">
<global-results>
<result name="input">/login.jsp</result>
</global-results>
</package>
<package name="registerUser" namespace="/registerUser" extends="defaultResult">
<action name="registerAction" class="cn.domain.User" method="register">
<result name="success">/WEB-INF/success.jsp</result>
</action>
</package>
<package name="find" namespace="/findUser" extends="defaultResult">
<action name="findAction" class="cn.domain.User" method="find">
<result name="success">/WEB-INF/success.jsp</result>
</action>
</package>
</struts>
示例2:minNum字段的值必须小于maxNum字段的值
动作类
package cn.action;
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private Integer minNum;//验证minNum必须必maxNum小
private Integer maxNum;
public String register(){
System.out.println("执行了动作方法register()");
ActionContext.getContext().put("message", "通过");
return Action.SUCCESS;
}
public String find(){
System.out.println("执行了动作方法find()");
ActionContext.getContext().put("message", "你只填充了username属性给User模型(Bean对象)但没有被验证,因为find()动作不会被验证");
return Action.SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getMinNum() {
return minNum;
}
public void setMinNum(Integer minNum) {
this.minNum = minNum;
}
public Integer getMaxNum() {
return maxNum;
}
public void setMaxNum(Integer maxNum) {
this.maxNum = maxNum;
}
}
struts.xml配置文件
<package name="registerUser" namespace="/registerUser" extends="defaultResult">
<action name="registerAction" class="cn.action.UserAction" method="register">
<result name="success">/WEB-INF/success.jsp</result>
</action>
</package>
UserAction-registerAction-validation.xml验证器配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<!-- 此种方式是无字段的验证,必须用此方式写,注意此处报错了的话,属于动作错误,前台只能通过<s:actionerror/>标签取出,并显示-->
<validator type="expression"><!-- OGLN表达式的验证器 -->
<param name="expression"><!-- 注入值"expression"表示使用表达式,内容中minNum和maxNum是动作类中的属性 -->
<![CDATA[minNum<maxNum]]><!-- 表示动作类中 minNum属性的值必须大于maxNum属性的值-->
</param>
<message>最大值必须大于最小值</message><!--错误提示信息-->
</validator>
</validators>
struts2提供的验证器
struts2提供的验证器在xwork-core-x.x.x.jar/com.opensymphony.xwork2.validator.validators/default.xml文件中有定义16个验证器。
<validators>
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
<validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
<validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
<validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
<validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
<validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
<validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
<validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
<validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
<validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
<validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
<validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
<validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
<validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
<validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>
验证器的说明
requiredstring 求此属性的值不能是null并且不是能””空字符串,默认情况下会对此字段的值去掉前后的空格,也可以用<param name="trim">true</param>进行设置是否去掉前后空格.
int 整数校验器,要求此属性的值必须是整数,并且在指定的范围,用param标签向验证器中注入min和max的值,如:<param name="min">1</param>
<param name="max">100</param>,那么整数的范围就是1~100之间了,如果没有指定范围,那么表示最大值和最小值无穷大和无穷小
内置验证器详细描述
struts2为我们共内置了16个验证器,且全部是基于字段的验证器。
required 验证器
功能
用来验证某个给定的字段的值不是null。注意,空字符串不是null。
参数
参数名 | 类型 | 默认值 | 值的类型 | 必须的 | 描述 |
fieldName | String |
| 要验证的属性名 | no | 要验证的字段名 |
(用法见后面的说明)
示例
页面:
<s:fielderror/>
<s:form action="validate">
<s:textfield name="userName" label="用户名"></s:textfield>
<s:submit value="登录"></s:submit>
</s:form>
动作类:
import com.opensymphony.xwork2.ActionSupport;
public class ValidationAction extends ActionSupport {
private static final long serialVersionUID = 6877330242746547448L;
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
<validators>
<field name="password">
<field-validator type="required">
<message>The password field is required!</message>
</field-validator>
</field>
</validators>
运行结果:
说明
<validators>
<validator type="required">
<param name="fieldName">password</param>
<message>The password field is required!</message>
</validator>
</validators>
requiredstring 验证器
功能
验证给定的字段的值既不是null、也不是能空字符串。
参数
参数名 | 类型 | 默认值 | 值的类型 | 必须的 | 描述 |
fieldName | String |
| 要验证的属性名 | no | 要验证的字段名 |
trim | Boolean | true | 是否去掉输入值前后的空格 | no | 验证前是否要去掉前导和尾缀的空白字符 |
页面:
<s:form action="validate">
<s:textfield name="userName" label="用户名" required="true" requiredposition="left"></s:textfield>
<s:password name="password" label="密码" required="true" requiredposition="left"></s:password>
<s:submit value="登录"></s:submit>
</s:form>
动作类:
import com.opensymphony.xwork2.ActionSupport;
public class ValidationAction extends ActionSupport {
private static final long serialVersionUID = 6877330242746547448L;
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
验证配置文件:
<validators>
<field name="userName">
<field-validator type="requiredstring">
<message>Please input the userName!</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<param name="trim">false</param>
<message>Please input the password!</message>
</field-validator>
</field>
</validators>
运行结果:
说明
验证配置文件的另外一种写法:
<validators>
<validator type="requiredstring">
<param name="fieldName">userName</param>
<message>Please input the userName!</message>
</validator>
<validator type="requiredstring">
<param name="fieldName">password</param>
<param name="trim">false</param>
<message>Please input the password!</message>
</validator>
</validators>
int 验证器
功能
用来验证某个字段的值是否可以被转换为一个整数。若指定参数,还验证是否在允许的范围内。
参数
参数名 | 类型 | 默认值 | 值的类型 | 描述 |
fieldName | String |
| 要验证的属性名 | 要验证的字段名 |
min | Integer |
| 最小值 | 允许的最小值。若没有给出该参数则无限制 |
max | Integer |
| 最大致 | 允许的最大值。若没有给出该参数则无限制 |
设置此参数用<param name="min">18</param>标签的方式进行注入
示例
页面:
<s:form action="validate">
<s:textfield name="age" label="年龄"></s:textfield>
<s:submit value="登录"></s:submit>
</s:form>
动作类:
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
验证配置文件:
<validators>
<field name="age">
<field-validator type="int">
<param name="max">60</param>
<message>The age must be between ${min} and ${max}</message>
</field-validator>
</field>
</validators>
运行结果:
说明
验证配置文件的另外一种写法:
<validators>
<validator type="int">
<param name="fieldName">age</param>
<param name="min">18</param>
<param name="max">60</param>
<message>The age must be between ${min} and ${max}</message>
</validator>
</validators>
long 验证器
功能
用来验证某个字段的值是否可以被转换为一个长整数。若指定参数,还验证是否在允许的范围内。
参数
参数名 | 类型 | 默认值 | 值的类型 | 描述 |
fieldName | String |
| 要验证的属性名 | 要验证的字段名 |
min | Long |
| 最小值 | 允许的最小值。若没有给出该参数则无限制 |
max | Long |
| 最大致 | 允许的最大值。若没有给出该参数则无限制 |
short 验证器
功能
用来验证某个字段的值是否可以被转换为一个短整数。若指定参数,还验证是否在允许的范围内。
参数
参数名 | 类型 | 默认值 | 值的类型 | 描述 |
fieldName | String |
| 要验证的属性名 | 要验证的字段名 |
min | Short |
| 最小值 | 允许的最小值。若没有给出该参数则无限制 |
max | Short |
| 最大致 | 允许的最大值。若没有给出该参数则无限制 |
double 验证器
功能
用来验证某个字段的值是否可以被转换为一个双精度浮点数。若指定参数,还验证是否在允许的范围内。
参数
参数名 | 类型 | 默认值 | 描述 |
fieldName | String |
| 要验证的字段名 |
minInclusive | Double |
| 允许的最小值。若没有给出该参数则无限制(含最小值) |
maxInclusive | Double |
| 允许的最大值。若没有给出该参数则无限制(含最大值) |
minExclusive | Double |
| 允许的最小值。若没有给出该参数则无限制(不含最小值) |
maxExclusive | Double |
| 允许的最大值。若没有给出该参数则无限制(不含最大值) |
示例
<s:form action="validate">
<s:textfield name="percentage1" label="百分比1"></s:textfield>
<s:textfield name="percentage2" label="百分比2"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private Double percentage1;
private Double percentage2;
public Double getPercentage1() {
return percentage1;
}
public void setPercentage1(Double percentage1) {
this.percentage1 = percentage1;
}
public Double getPercentage2() {
return percentage2;
}
public void setPercentage2(Double percentage2) {
this.percentage2 = percentage2;
}
验证配置文件:
<validators>
<field name="percentage1">
<field-validator type="double">
<param name="minInclusive">20.1</param>
<param name="maxInclusive">50.1</param>
<message> The age must be between ${ minInclusive } and ${ maxInclusive }(含)</message>
</field-validator>
</field>
<field name="percentage2">
<field-validator type="double">
<param name="minExclusive">0.345</param>
<param name="maxExclusive">99.987</param>
<message> The age must be between ${ minExclusive } and ${ maxExclusive }(不含)</message>
</field-validator>
</field>
</validators>
运行结果:
说明
<validators>
<validator type="double">
<param name="fieldName">percentage1</param>
<param name="minInclusive">20.1</param>
<param name="maxInclusive">50.1</param>
<message> The age must be between ${ minInclusive } and ${ maxInclusive }(含)</message>
</validator>
<validator type="double">
<param name="fieldName">percentage2</param>
<param name="minExclusive">0.345</param>
<param name="maxExclusive">99.987</param>
<message> The age must be between ${ minExclusive } and ${ maxExclusive }(不含)</message>
</validator>
</validators>
date 验证器
功能
用来确保给定的日期字段的值落在一个给定的范围内。
参数
参数名 | 类型 | 默认值 | 描述 |
fieldName | String |
| 要验证的字段名 |
min | java.util.Date |
| 允许的最小值。若没有给出该参数则无限制(含最小值) |
max | java.util.Date |
| 允许的最大值。若没有给出该参数则无限制(含最大值) |
示例
页面:
<s:form action="validate">
<s:textfield name="birthday" label="出生日期"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
验证配置文件:
<validators>
<field name="birthday">
<field-validator type="date">
<param name="min">2011-01-01</param><!--此处的日期格式,必须匹配当前的日期类型转换器-->
<param name="max">2011-12-31</param>
<message>日期必须为2011年</message>
</field-validator>
</field>
</validators>
运行结果:
说明
验证配置文件的另外一种写法:
<validators>
<validator type="date">
<param name="fieldName">birthday</param>
<param name="min">2011-01-01</param>
<param name="max">2011-12-31</param>
<message>日期必须为2011年</message>
</validator>
</validators>
expression 验证器
功能
用于验证是否满足一个OGNL表达式。这是一个非字段的验证。只有给定的参数的返回值是true时才能验证通过。验证不通过时产生一个动作错误,因此要显示该错误,需要使用<s:actionerror/>标签。
参数
参数名 | 类型 | 默认值 | 描述 |
expression | String |
| OGNL表达式,只有该表达式为true才能验证通过, |
示例
页面:
<s:actionerror/>
<s:form action="validate">
<s:textfield name="minNumber" label="最小值"></s:textfield>
<s:textfield name="maxNumber" label="最大值"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private Integer minNumber;
private Integer maxNumber;
public Integer getMinNumber() {
return minNumber;
}
public void setMinNumber(Integer minNumber) {
this.minNumber = minNumber;
}
public Integer getMaxNumber() {
return maxNumber;
}
public void setMaxNumber(Integer maxNumber) {
this.maxNumber = maxNumber;
}
验证配置文件:
<validators>
<validator type="expression">
<param name="expression"><!--此处的maxNumber和minNumber的值取的是动作类中的属性的值 -->
<![CDATA[maxNumber>minNumber]]> <!--只要是表达式,最好用<![CDATA[表达式]]>括起来-->
</param>
<message>最大值必须大于最小值</message>
</validator>
</validators>
运行结果:
说明
该验证器没有字段形式的写法。要进行字段验证,请使用fieldexpression验证器。
fieldexpression 验证器
功能
用于验证某个字段是否满足一个OGNL表达式。这是一个基于字段的验证。只有给定的参数的返回值是true时才能验证通过。验证不通过时产生一个字段错误。
参数
参数名 | 类型 | 默认值 | 描述 |
fieldName | String |
| 要验证的字段名 |
expression | String |
| OGNL表达式,只有该表达式为true才能验证通过 |
示例
页面:
<s:form action="validate">
<s:textfield name="minNumber" label="最小值"></s:textfield>
<s:textfield name="maxNumber" label="最大值"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private Integer minNumber;
private Integer maxNumber;
public Integer getMinNumber() {
return minNumber;
}
public void setMinNumber(Integer minNumber) {
this.minNumber = minNumber;
}
public Integer getMaxNumber() {
return maxNumber;
}
public void setMaxNumber(Integer maxNumber) {
this.maxNumber = maxNumber;
}
验证配置文件:
<validators>
<field name="maxNumber">
<field-validator type="fieldexpression">
<param name="expression"><!--此处的maxNumber和minNumber的值取的是动作类中的属性的值 -->
<![CDATA[maxNumber>minNumber]]><!--只要是表达式,最好用<![CDATA[表达式]]>括起来-->
</param>
<message>最大值必须大于最小值1</message>
</field-validator>
</field>
</validators>
运行结果:
说明
验证配置文件的另外一种写法:
<validators>
<validator type="fieldexpression">
<param name="fieldName">maxNumber</param>
<param name="expression"><!--此处的maxNumber和minNumber的值取的是动作类中的属性的值 -->
<![CDATA[maxNumber>minNumber]]> <!--只要是表达式,最好用<![CDATA[表达式]]>括起来-->
</param>
<message>最大值必须大于最小值</message>
</validator>
</validators>
email 验证器
功能
用来验证给定的字段是否符合一个Email的规范。它的正则表达式为
\\b(^[_A-Za-z0-9-](\\.[_A-Za-z0-9-])*@([A-Za-z0-9-])+((\\.com)|(\\.net)|(\\.org)|(\\.info)|(\\.edu)|(\\.mil)|(\\.gov)|(\\.biz)|(\\.ws)|(\\.us)|(\\.tv)|(\\.cc)|(\\.aero)|(\\.arpa)|(\\.coop)|(\\.int)|(\\.jobs)|(\\.museum)|(\\.name)|(\\.pro)|(\\.travel)|(\\.nato)|(\\..{2,3})|(\\..{2,3}\\..{2,3}))$)\\b
参数
参数名 | 类型 | 默认值 | 描述 |
fieldName | String |
| 要验证的字段名 |
示例
页面:
<s:form action="validate">
<s:textfield name="email" label="邮箱"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
验证配置文件:
<validators>
<field name="email">
<field-validator type="email">
<message>请输入正确的邮箱</message>
</field-validator>
</field>
</validators>
运行结果:
说明
<validators>
<validator type="email">
<param name="fieldName">email</param>
<message>请输入正确的邮箱</message>
</validator>
</validators>
url验证器
功能
用来验证给定的字段值是否是一个合法的URL地址。最基本的格式必须为:http://www.baidu.com
参数
参数名 | 类型 | 默认值 | 描述 |
fieldName | String |
| 要验证的字段名 |
示例
页面:
<s:form action="validate">
<s:textfield name="url" label="个人主页"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
验证配置文件:
<validators>
<field name="url">
<field-validator type="url">
<message>请输入正确的地址</message>
</field-validator>
</field>
</validators>
运行结果:
说明
验证配置文件的另外一种写法:
<validators>
<validator type="url">
<param name="fieldName">url</param>
<message>请输入正确的地址</message>
</validator>
</validators>
visitor 验证器
功能
该验证程序可以提高代码的可重用性,你可以利用它把同一个验证程序配置文件用于多个动作。
示例
页面:
<s:form action="customer_save">
<s:textfield name="address.streetName" label="街道"></s:textfield>
<s:submit></s:submit>
</s:form>
动作类:
public class Customer extends ActionSupport {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
public class Address {
private String streetName;
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
}
验证配置文件:
Address-validation.xml
<validators>
<field name="streetName">
<field-validator type="requiredstring">
<message>请输入正确街道地址</message>
</field-validator>
</field>
</validators>
Customer-validation.xml
<validators>
<field name="address">
<field-validator type="visitor">
<message>Address:</message>
</field-validator>
</field>
</validators>
运行结果:
stringlength 验证器
功能
用来验证一个非空的字段值是不是有足够的长度。
参数
参数名 | 类型 | 默认值 | 描述 |
minLingth | Int | -1 | 此字符串的最小长度(必须有此参数)(含此长度) |
maxLingth | int | -1 | 此字符串的最大长度(必须有此参数)(含此长度) |
trim | Boolean | true | 验证前是否要去掉前导和尾缀的空白字符 |
regex验证器
功能
用来检查给定字段是否与给定的正则表达式相匹配。正则表达式的详细内容可以参考JDK的java.util.regex.Pattern类。
参数
参数名 | 类型 | 默认值 | 描述 |
fieldname | String |
| 要验证的字段名 |
expression | String |
| 正则表达式。此参数是必须的 |
caseSensitive | Boolean | true | 是否区分大小写的情况 |
trim | Boolean | true | 验证前是否要去掉前导和尾缀的空白字符 |
示例
页面:
<s:form action="validate">
<s:textfield name="userName" label="用户名"></s:textfield>
<s:submit value="保存"></s:submit>
</s:form>
动作类:
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
验证配置文件:
<validators>
<field name="userName">
<field-validator type="regex">
<param name="expression"><![CDATA[([aAbBcCdD][123][eEfFgG][456])]]></param><!--只要是表达式,最好用<![CDATA[表达式]]>括起来-->
<message> 用户名必须符合规范</message>
</field-validator>
</field>
</validators>
运行结果:
说明
验证配置文件的另外一种写法:
<validators>
<validator type="regex">
<param name="fieldName">userName</param>
<param name="expression"><![CDATA[([aAbBcCdD][123][eEfFgG][456])]]></param><!--只要是表达式,最好用<![CDATA[表达式]]>括起来-->
<message> 用户名必须符合规范</message>
</validator>
</validators>
自定义基于XML验证器
1、 自定义验证器的类应放在validators包中并且类名应遵循 以Validator字符结尾的命名规则。
2、 自定义验证器的类应继承com.opensymphony.xwork2.validator.validators.ValidatorSupport(用于编写无属性验证的验证器)或com.opensymphony.xwork2.validator.validators.FieldValidatorSupport(用于编写有属性验证的验证器,推荐继承此类)类,并重写此类中的validate(Object object)方法。
3、 在WEB-INF/classes目录下(对应的开发目录为src)创建一个名为validators.xml(固定写法) 的自定义验证器注册配置文件,在其中可以注册自定义的验证器。
validators.xml验证器注册配置文件的模板:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator Config 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">
<validators><!--注册自定义验证器-->
<validator name="为此验证器取一个名称" class="自定义验证器的完整类名"/>
</validators>
示例:(用自定义验证器,验证密码中 必须包含数字、大写字母、小写字母,并且密码的长度可以在验证器配置文件中注入)(有字段的验证)
自定义验证器
package cn.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;
//定义一个我自己的密码验证器(必须继承FieldValidatorSupport类)
public class MyPasswordValidator extends FieldValidatorSupport {
//设置密码的最小长度
private Integer minLength = -1;
//设置密码的最大长度
private Integer maxLength = -1;
private boolean trim = true;
//重写父类的 validate(Object object)方法,此方法会将 动作类的示例对象传入
@Override
public void validate(Object object) throws ValidationException {
//得到要验证的属性名
String fieldName = super.getFieldName();
//通过属性名和此动作对象,获取到此对象此属性的值(通过内省做的)
String value = (String)super.getFieldValue(fieldName, object);
//判断设置的最小长度和最大长度是否合法
if(minLength < 0 || maxLength <= minLength){
throw new RuntimeException("MyPasswordValidator验证器中,maxLength属性的值必须大于minLength属性的值");
}
//判断输入的值是否为null
if(value == null){
super.addFieldError(fieldName, object);//向属性异常集合中添加异常描述
return;
}
//判断是否去掉输入值的前后空格
if(trim){
value = value.trim();
}
//判断输入值的长度是否合法
if(value.length()<minLength || value.length()>maxLength){
super.addFieldError(fieldName, object);//向属性异常集合中添加异常描述
return;
}else{
//判断此输入值中是否都包含了 数字、小写字母、大写字母
boolean flag = false;;
String[] regexs = new String[]{"^.*[0-9]+.*$", "^.*[a-z]+.*$", "^.*[A-Z]+.*$"};
for(String regex : regexs){
flag = value.matches(regex);
if(!flag){
break;
}
}
if(!flag){//如果只要有一种类型没有包含
super.addFieldError(fieldName, object);//向属性异常集合中添加异常描述
}
return;
}
}
public Integer getMinLength() {
return minLength;
}
public void setMinLength(Integer minLength) {
this.minLength = minLength;
}
public Integer getMaxLength() {
return maxLength;
}
public void setMaxLength(Integer maxLength) {
this.maxLength = maxLength;
}
public boolean isTrim() {
return trim;
}
public void setTrim(boolean trim) {
this.trim = trim;
}
}
验证器的配置文件(UserAction-registerAction-validation.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">2</param>
<param name="maxLength">10</param>
<message>用户名的长度必须为2~10位</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>密码不能为空</message>
</field-validator><!-- 引用了我自己的密码验证器 -->
<field-validator type="mypasswordvalidate">
<param name="minLength">5</param><!-- 向验证器对象中注入最小的长度 -->
<param name="maxLength">10</param><!-- 向验证器对象中注入最大的长度 -->
<param name="trim">true</param>
<message>密码必须包含小写字母、大小字母、数字,并且是${minLength}~${maxLength}位,你输入的为:${password}</message>
</field-validator>
</field>
</validators>
验证器的注册文件(validators.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator Config 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">
<validators>
<!-- 注册自定义验证器 -->
<!-- name=为此自定义验证器取一个名称 ,class=自定义验证的完整名称-->
<validator name="mypasswordvalidate" class="cn.validators.MyPasswordValidator"/>
</validators>
示例2:验证minNumber属性的值必须比maxNumber属性的值小(无字段的验证)
自定义验证器
package cn.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.ValidatorSupport;
//定义一个我自己的验证器,通过传入的字段名,验证最小值必须小于最大值(无字段验证器)(必须继承ValidatorSupport类)
public class MyValidator extends ValidatorSupport {
//定义两个变量,用于验证时注入此最小属性名名称,和最大属性的名称
private String minFieldName;
private String maxFieldName;
@Override
public void validate(Object object) throws ValidationException {
//通过传入的最小值的属性名称,再通过内省的方式获取到此对象中此属性名的值
Integer minNumber = (Integer)super.getFieldValue(minFieldName, object);
//通过传入的最大值的属性名称,再通过内省的方式获取到此对象中此属性名的值
Integer maxNumber = (Integer)super.getFieldValue(maxFieldName, object);
//判断最获取到的值是否为null
if(minNumber == null || maxNumber == null){
super.addActionError(object);//如果为null,就在动作异常Map集合中添加异常信息
return;
}else if(minNumber >= maxNumber){//判断最小值是否小于最大值
super.addActionError(object);//如果最小值大于等于了最大值,那么就向动作异常Map结婚中添加异常信息
return;
}
}
public String getMinFieldName() {
return minFieldName;
}
public void setMinFieldName(String minFieldName) {
this.minFieldName = minFieldName;
}
public String getMaxFieldName() {
return maxFieldName;
}
public void setMaxFieldName(String maxFieldName) {
this.maxFieldName = maxFieldName;
}
}
验证器的配置文件(UserAction-registerAction-validation.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">2</param>
<param name="maxLength">10</param>
<message>用户名的长度必须为2~10位</message>
</field-validator>
</field>
<!-- <field name="password">
<field-validator type="requiredstring">
<message>密码不能为空</message>
</field-validator>
<field-validator type="mypasswordvalidate">
<param name="minLength">5</param>
<param name="maxLength">10</param>
<param name="trim">true</param>
<message>密码必须包含小写字母、大小字母、数字,并且是${minLength}~${maxLength}位,你输入的为:${password}</message>
</field-validator>
</field> -->
<!-- 引用了我自己的验证器,验证minNumber字段的值必须比maxNumber字段的值小 -->
<validator type="myvalidate">
<param name="minFieldName">minNumber</param><!-- 向验证器对象中注入最小值的字段名 -->
<param name="maxFieldName">maxNumber</param><!-- 向验证器对象中注入最大值的字段名 -->
<message>最小值必须小于最大值</message>
</validator>
</validators>
验证器的注册文件(validators.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator Config 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">
<validators>
<!-- 注册自定义验证器 -->
<!-- name=为此自定义验证器取一个名称 ,class=自定义验证的完整名称-->
<validator name="myvalidate" class="cn.validators.MyValidator"/>
</validators>
struts2中的国际化(i18n)
struts2国际化的占位符
在国际化语言资源properties配置文件中,映射关系的value中可以插入占位符“{0}
如:
语言资源properties文件中的映射关系为:
welcome={0},欢迎来到成都{1}
//{0}表示第一个占位符,{1}表示第二个占位符
在JSP页面中取出此value,并注入占位符的值
<s:text name=”welcome”>
<s:param>何春霖</s:param><!--注入value值中第一个占位符的值-->
<s:param>玩耍</s:param><!--注入value值中第二个占位符的值-->
</s:text>
//那么在浏览器中显示的值为:何春霖,欢迎来到成都玩耍
在动作类中取出此value,并注入占位符的值
//前提是此动作类必须继承ActionSupport这个类
String value = super.getText(”welcome”,new String[]{”何春霖”,”玩耍”});
System.out.println(value);
//那么此处在控制台中打印的值为:何春霖,欢迎来到成都玩耍
struts2国际化中查找语言资源文件的优先级
当我们在用映射关系中的key取对应的value时,struts2会用此key在当前包中的ActionClassName_language_country.properties(作用于指定动作类的) 语言资源配置文件中去查找,如果没有此key。
那么struts2会用此key沿着当前包一层一层(就是从当前的包一直找到根包,如:cn.action包,先查找action包如果没有再查找cn包,因为cn是根包,如果没有此key就不再向上查找了)的往上查找命名规则为:package_language_country.properties(package_为固定写法,并且此配置文件作用于包和子包)的语言资源配置文件,如果这些包中还是没有此key。
那么就会到struts中默认的常量配置文件(struts2-core-x.x.jar工具中的org.apache.struts2包中default.properties)中去找struts.custom.i18n.resources常量指定的基名,用此基名在WEB-INF/classes目录中查找此 基名_language_country.peroperties(作用于全局的语言资源配置文件)配置文件中去查找此key,如果还是没有此key,那么将会抛异常
全局范围内的国际化语言资源properties配置文件
此文件应放在WEB-INF/classes目录(对应的开发目录为src)下,此语言配置文件的命名规则为: 基名_language_country.properties(基名_语言_国家,如:scxh_zh_CN.properties)。
全局范围内的国际化语言资源配置文件的 基名,需要在struts.xml配置文件中的struts标签标签下用:<constant name=" struts.custom.i18n.resources" value="基名1,基名2..."></constant>标签进行注册,全局范围内的国际化语言资源配置文件需要经过注册后才能使用。
示例:(或者参照struts3.07_i18n工程)
现在(WEB-INF/class)src目录下有两个语言资源配置文件(这就说明了作用于全局),一个是中文的(scxh_zh_CN.properties),一个是英文的(scxh_en_US.properties)
在JSP页面取出此值:
<!-- i18n标签name属性指定的是范围,这里直接写基名,表示从全局范围中的语言资源配置文件去找 -->
<s:i18n name="scxh">
<!-- 从此范围的语言资源配置文件中去取key为welcome的值,(至于国家和语言,struts会通过http协议的头信息中获取) -->
<s:text name="welcome">
<!-- 向此值中的占位符中注入值,如果没有占位符就不用注入值了 -->
<s:param>何春霖</s:param>
<s:param>玩耍</s:param>
</s:text>
</s:i18n>
在动作类中取出此值
public class I18nAction extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
//至于此处是从哪个范围中查找的语言资源配置文件,请求参照:struts2国际化中查找语言资源文件的优先级
//获取到全局国际化语言资源配置文件中的welcomekey中的值,并向占位符注入值(至于国家和语言,struts会通过http协议的头信息中获取)
String value = super.getText("welcome",new String[]{"何春霖","玩耍"});
//打印取出来的值
System.out.println(value);
return Action.SUCCESS;
}
}
包范围内的国际化语言资源properties配置文件(推荐使用)
包范围内的语言资源配置文件,同样作用于所有的子包下(也就说如果一个包用有了此配置文件,那么这个包的所有子包中的动作类也可以访问此语言资源配置文件)。
此文件应放在某一个包下,那么它将作用用这个包和这个包的所有子包,此语言资源配置文件的命名规则为:package_language_country.properties(package_是固定写法,如:package_语言_国家)
示例:(或者参照struts3.08_i18n2工程)
现在cn.action包下有两个语言资源配置文件,一个是中文的(package_zh_CN.properties),一个是英文的(package_en_US.properties)
在JSP页面取出此值:
<!-- i18n标签name属性指定的是范围,这里写的包名/基名,表示从包范围内的语言资源配置文件去找 -->
<s:i18n name="cn/action/package">
<!-- 从此范围的语言资源配置文件中去取key为welcome的值,(至于国家和语言,struts会通过http协议的头信息中获取) -->
<s:text name="welcome">
<!-- 向此值中的占位符中注入值,如果没有占位符就不用注入值了 -->
<s:param>何春霖</s:param>
<s:param>玩耍</s:param>
</s:text>
</s:i18n>
在本包的动作类中取出此值(本包为:cn.action)
package cn.action;
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
public class I18nAction extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
//至于此处是从哪个范围中查找的语言资源配置文件,请求参照:struts2国际化中查找语言资源文件的优先级
//获取到的是包范围内的国际化语言资源配置文件中的welcomekey中的值,并向占位符注入值(至于国家和语言,struts会通过http协议的头信息中获取)
String value = super.getText("welcome",new String[]{"何春霖","玩耍"});
//打印取出来的值
System.out.println(value);
return Action.SUCCESS;
}
}
在子包的动作类中取出此值(子包为: cn.action.sonaction;)
package cn.action.sonaction;
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
public class I18nActionSon extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
//在语言资源配置文件的包的所有子包下的动作类中也可取出此语言资源配置文件中的值
//因为包范围内的语言资源文件应放在某一个包下,那么它将作用用这个包和这个包的所有子包(还需遵守包范围内的语言配置文件的命名规则)
String value = super.getText("welcome",new String[]{"何春霖","玩耍"});
//打印取出来的值
System.out.println("这是在语言配置文件的包的子包中取出的welcomekey的值:"+value);
return Action.SUCCESS;
}
}
动作范围内的国际化语言资源properties配置文件
动作内的国际化语言资源配置文件,只作用与某一个动作类,并且必须和此动作类放在同一个包下
此语言资源配置文件的命名规则为:ActionClassName_language_country(动作类名_语言_国家,如:I18nAction_zh_CN)
动作范围内的语言资源配置文件,因为只作用于某一个动作类,所以在JSP中无法直接访问,但可以通过动作类将值存在域中,然后通过JSP访问域中的值
示例:(或者参照struts3.09_i18n3工程)
现在cn.action包下有两个语言资源配置文件,一个是中文的(I18nAction_zh_CN.properties),一个是英文的(I18nAction_en_US.properties)
在动作类中取出此值
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
public class I18nAction extends ActionSupport implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
//至于此处是从哪个范围中查找的语言资源配置文件,请求参照:struts2国际化中查找语言资源文件的优先级
//获取到的是动作范围内的国际化语言资源配置文件中的welcomekey中的值,并向占位符注入值(至于国家和语言,struts会通过http协议的头信息中获取)
String value = super.getText("welcome",new String[]{"何春霖","玩耍"});
//打印取出来的值
System.out.println(value);
return Action.SUCCESS;
}
}
此处的值应是:
attachment;filename="${downFileName}"
不然无法在浏览器中弹出下载框,attachment表示告诉浏览器以下载的形式打开,此处设置的是:content-disposition : HTTP协议下载头的值
struts2中的OGNL表达式
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。Struts2框架使用OGNL作为默认的表达式语言。
如果要在JSP中使用OGNL表达式需要用:<s:property value="%{#request.test1}"/>标签,来使用OGNL表达式
如果要在其他标签中的属性中使用OGNL表达式,但此属性又不支持OGNL表达式,那么此属性的值直接放入:<s:property value="%{ognl表达式}"/> 此标签即可
以下对象在操作类中同样可以获取到
增加:OGNL表达式的使用规范,如果要 使用OGNL表达式那么请用%{}扩起来,如果不用OGNL表达式,并要表示是一个字符串,那么请在双引号中再用单引号括起来
如:
使用OGNL表达式:
<s:set value="%{#session.val}" scope="page" var="val"/>
不使用OGNL表达式:
<s:set value="’abc’" scope="page" var="val"/>
相对EL表达式,它提供了平时我们需要的一些功能,如:
支持对象方法调用,如xxx.sayHello();
支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名|值名],例如:@java.lang.String@format(‘foo %s’,’bar’)或@cn.itcast.Constant@APP_NAME;
操作集合对象,创建和三次
Ognl(ActionContext对象)的结构
Ognl有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了java.utils.Map接口,在Struts2中上下文(Context)对象是ActionContext(也是java.utils.Map的子类)
结构图:
|
|--valueStack(root:list集合) 此对象是一个特殊的对象,在此对象中有一个list集合,集合中存放了一些对象
|
|--apliaction 是serlvet中ServletContext对象底层维护的Map集合
|
|--session 是servlet中HttpServlet对象底层维护的Map集合
ActionContext(Map,这里称为action域)--- |
|--request 是serlvet中request对象底层维护的Map集合(但此request中可以获取到以上所有容器中的数据,详情请参考
| valueStack对象中的findValue(key)方法)
|--attr 此对象中可以获取到request域中的数据(因为request域可以获取到以上所有容器中的数据,那么此属性也可以获取到)
|
|--parameters 是servlet中request对象请求参数中底层维护的Map集合(就是request. getParameter(key)方法底层维护的Map,
| 注意此Map是一个<String,String[]>类型的Map)
|
以上对象的详细说明
ActionContext对象(action域(Map栈))
如果要查看此对象的结构图,可以在JSP页面用<s:debug></s:debug>标签进行查看,(用debug标签时,有两种可能会报错,
1. 当栈顶有集合时,用debug标签会报错
2. 当结合hibernate框架时,如果一个对象是懒加载,那么用debug标签时debug标签会遍历此对象的所有属性,所以有可能出现session以关闭等问题)。
debug标签上面的图显示的是ValueStack对象中名为root的list集合的结构图,下面的图是ActionContext对象底层维护的Map集合的结构图。
此对象实现了java.utils.Map接口,所以此对象本身就是一个Map集合,此Map集合并不是request域中底层维护的Map集合。request域中能够获取到此Map集合中的数据和此Map集合中valueStack对象中root这个List集合中对象的属性,但此Map集合却不能获取到request域中的数据,原因如下:(必须要通过request底层维护的Map集合才能过去到)
struts2对request对象中的getAttribute(String key)进行了重写
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
public StrutsRequestWrapper(HttpServletRequest request){
super(request);
}
@Override
public Object getAttribute(String s){//传入要在域中查找的key值
…
//获取到ActionContext对象
ActionContext ctx = ActionContext.getContext();
//先到request对象域中去查找是否有此映射关系
Object attribute = super.getAttribute(s);
if(ctx!=null){
//如果request对象域中没有查找到此映射关系
if(attribute==null){
…
ValueStack stack = ctx.getValueStack();
//将到valueStack对象中的root这个list集合,和ActionContext中的Map集合(action域)中去找此key的值,如果还是没有找到就返回null,这也是为什么EL表达式能访问ActionContext对象底层维护的Map集合和valueStack对象中的list集合中的对象中的属性的值了
attribute = stack.findValue(s);
…
}
}
return attribute;
}
}
此对象中的Map集合中最基本的对象有valueStack对象、apliaction Map集合、session Map集合、request Map集合、attr对象、parameters Map集合
在JSP页面用OGNL表达式获取到ActionContext对象中Map集合中(action域)的数据
1. 直接在jsp页面用<s:property value=""/>标签进行获取,如:<s:property value="#request"/>获取到的就是request对象中的Map集合
2. 如果在jsp页面要获取valueStack对象中的root这个list集合中的对象的属性,那么在GNNL表达式中可以不用在前面加上#valueStack.root,在表达式中直接写此list集合中的属性名即可。如:<s:property value="此list集合中对象的属性名"/>。如果在此list集合中有两个或以上的对象都有此属性名,那么获取到的永远都是list集合中最靠近栈顶(栈顶的下标是(0))并且有此属性名的那个对象的此属性的值,如果一定要指定获取此list集合中某个下标对象的此属性的值,只需要在OGNL表达式前面加上一个[下标].即可。 如:<s:property value="[5].此list集合中对象的属性名"/>,那么获取到的是此list集合中下标为5的对象中此属性名的值
valueStack对象
此对象也可以再动作类中通过ActionContext.getContext().getValueStack();的方式获取到;
还可以用CompoundRoot root = ActionContext.getContext().getValueStack().getRoot();的方式获取到其中的root这个list集合,这个root是继承了List这个接口
此对象比较特殊,此对象中维护了一个名为root的list集合。当struts接受一个请求时,会迅速创建ActionContext,ValueStack,动作对象。然后把 动作对象 存放进ValueStack的root这个list集合中,所以 动作对象 的变量可以被OGNL或者request对象的getAttribute(key)方法访问。并且存放在此list集合中的对象不需要在OGNL表达式前面加上”#valueStack.root”前缀,如果要访问某一个对象,直接下标,如:<s:property value="[5]"/>,将访问的是这个list集合中下标为5的对象,如果要访问这个对象中的属性,在OGNL表达式中直接写”[5].属性名”。
如果GONL表达式的写法为:<s:property value="name"/>,那么系统将会到此list集合中从下标为0(栈顶)的对象中开始找,如果此对象有此属性,就直接输出此属性的值,如果此对象没有此属性,那么就找此list集合中下标为1的对象,如果此对象还是没有此属性,那么就依次向下找,直到找到为止。
在动作类中可以用CompoundRoot root = ActionContext.getContext().getValueStack().getRoot(); root.add(Object); 的方式向此root对象栈中存入值,或者用ActionContext.getContext().getValueStack().setValue("属性名", "值");的方式改变此root对象栈中 的对象的属性的值(从栈顶开始找,如果此对象没有此属性,就找下一个对象,直到找到为止);
此valueStack(值栈)对象,还可以将一个对象压入到root(对象栈)的栈顶:ActionContext.getContext().getValueStack().push(Object);
findValue方法
此对象中还有一个findValue(key)的方法,此方法是在valueStack对象中的名为root的list集合 中去查找有此key属性的对象,如果list集合中没有,那么将到
action域(ActionContext中的Map集合)、session Map集合、apliaction Map集合,依次去查找此key的值,如果还是没有此key,那么就返回null
apliaction Map集合
此集合就相当于servlet中serlvetContext域对象中底层维护的Map集合
如果OGNL表达式要访问此Map集合中的数据,格式应为:<s:property value="#apliaction.key"/>
session Map集合
此集合就相当于servlet中HttpSession域对象中底层维护的Map集合
如果OGNL表达式要访问此Map集合中的数据,格式应为:<s:property value="#session.key"/>
request Map集合
此集合就相当于servlet中request域对象中底层维护的Map集合
如果OGNL表达式要访问此Map集合中的数据,格式应为:<s:property value="#request.key"/>
此request Map集合比较特殊因为struts2重写此request对象中的getAttribute(key)方法(上面已讲),所以此域不仅可以获取自己域中的数据,还可以获取valueStack对象中的名为root的list集合、action域(ActionContext中的Map集合)中的数据(是按此顺序来查找的)
但此 request还是可以通过action域来访问session Map和apliaction Map中的数据 如:
request访问session Map中的数据:<s:property value="#attr.session.key"/>
request访问session Map中的数据:<s:property value="#attr.apliaction.key"/>
attr 对象
此对象也可访问valueStack对象中的名为root的list集合、action域(ActionContext中的Map集合)、request Map、session Map和apliaction Map 集合中的数据
当使用<s:property value="#attr.key"/>OGNL表达式时,和使用<s:property value="#request.key"/>是一样的效果,但除了访问session Map和apliaction Map 集合中的数据
如:
attr访问session Map中的数据:<s:property value="#attr.key"/>
attr访问apliaction Map中的数据:<s:property value="#attr.key"/>
request访问session Map中的数据:<s:property value="#attr.session.key"/>
request访问session Map中的数据:<s:property value="#attr.apliaction.key"/>
parameters Map集合
此集合相当于request.getParameter(key)方法底层维护的请求结果Map集合(只能取出此Map集合中的数据,不能将其存入)
如果OGNL表达式要访问此Map集合中的数据,格式应为:<s:property value="#parameters.key"/>,获取的将是请求的值
注意:request.getParameter(key)方法底层维护的请求结果Map集合 是一个<String,String[]>这样的集合,在OGNL表达式取值时,取的是下标为0的值,而在ActionContext.getContext().getParameters(key)取的是一个String[]数组
Ognl表达式操作集合(Map/List)
<body>
<p>Ognl表达式操作List和Map集合</p>
<!-- 创建一个list集合 -->
<!-- valur的值是一个OGNL表达式,通过此种方式创建一个List集合, var的是此集合存储在域中的key,scope是指定此list集合应该存储在那个域中(缺省状况下是存储在action域中)
该属性可以接受application、session、request、page或action。-->
<s:set value="{'a','b','c'}" var="list1" scope="action"/>
<!-- 取出list1集合中下标为0的值 -->
<s:property value="#list1[0]"/><br/>
<s:set value="{'aa','bb','cc'}" var="list2" scope="session"/>
<!-- 取出list2集合中下标为0的值 -->
<s:property value="#session.list2[0]"/>
<p>list集合的遍历</p>
<s:iterator value="#session.list2" var="li">
<s:property value="#li"/>
</s:iterator>
<br/>
<!-- 创建一个Map集合 -->
<!-- 用OGNL表达式创建一个Map集合,前面必须加上“#”号,key和value之间用":"分割,映射关系和映射关系之间用","号分割 -->
<s:set value="#{'key1':'value1','key2':'value2','key3':'value3'}" var="map1" scope="session"/>
<p>按key 取出此Map中对应的value</p>
<s:property value="#session.map1.key1"/>
<p>Map集合的遍历</p>
<s:iterator value="#session.map1" var="entry"><!-- 遍历出来后是一个Entry对象 -->
<s:property value="#entry.key"/>=<s:property value="#entry.value"/><br/><!-- 获取此Entry对象的key和value -->
</s:iterator>
<s:debug></s:debug>
</body>
Ognl表达式中in和not in的用法
<body>
<p>ognl表达式中的in和not in的用法</p>
<!-- in和not in是判断一个对象是否在一个集合中 -->
<!-- 判断是对象是否在一个List集合中 -->
<!-- in的用法(存在返回true) -->
<s:if test="'a' in {'a','b','c'}">
<p>存在</p>
</s:if>
<s:else>
<p>不存在</p>
</s:else>
<!-- not in的用法(存在返回false) -->
<s:if test="'a' not in {'a','b','c'}">
<p>不存在</p>
</s:if>
<s:else>
<p>存在</p>
</s:else>
<s:debug></s:debug>
</body>
OGNL表达式的投影功能(一般不用,因为这是属于逻辑处理的业务,应该在 service层处理此种业务)
除了in和not in之外,OGNL还允许使用某个规则获得集合对象的子集,常用的有以下3个相关操作符。
?:获得所有符合逻辑的元素
^:获得符合逻辑的第一个元素
$:获得符合逻辑的最后一个元素
示例:
动作类
public class BookAction extends ActionSupport{
private List<Book> books;//其中有一个书的List集合
public String execute(){
books = new ArrayList<Book>();
//当调用动作方法时,向此书集合中添加书
books.add(new Book(“a”,”spring”,67));
books.add(new Book(“b”,”ejb”,15));
books.add(new Book(“c”,”java编程”,37));
books.add(new Book(“d”,”c语言”,25));
books.add(new Book(“e”,”HTML”,34));
}
}
JSP
<!--此处的OGNL表达式为,获取到动作对象中的books这个List集合,{}代表取出该集合的子元素(相当于就是遍历),?代表获取所有符合后面表达式(#this.price>35)的所有元素,
#this.price>35 表示从action中去取出当前正在被遍历的对象,然后获取其price属性,判断是否大于35,如果大于35就表示符合条件-->
<s:iterator value=“books.{?#this.price>35}”>
<s:property value=“title”/>-$<s:property value=“price”/><br/>
</s:iterator>
结果为:
spring – 67
java编程 - 37
在上面代码中,直接在集合后紧跟.{}运算符表明用于取出该集合的子集,{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合,需要对大集合books进行迭代,this代表当前迭代的元素。本例的表达式用于获取集合中价格大于35的书集合,?号代表输出所有符合条件的元素。
struts2中的模型驱动
模型驱动就是在动作类中填充此动作类中一个对象属性(就是此属性是一个对象)的值,在前面的<struts2中接收表单的参数>也有一种向动作类中的对象属性赋值的方法.但推荐使用此种方式来向 类型为对象的属性 赋值
缺点及解决办法:
此种方式,只能给动作类中的一个模型(对象属性)进行赋值,如果此动作类中有多个对象属性,那么将无法赋值,如果有这种情况,推荐使用<struts2中接收表单的参数>的方式向动作类中多个对象属性进行赋值
原理:
就是将此动作类中的对象属性(Bean对象)压入到valueStack(值栈对象)中root(对象栈)的栈顶,那么ModelDrivenInterceptor(modelDriven)拦截器就可以通过前台表单传入的值的一个Map集合的key(表单的name属性的值)的值,再通过ValueStack对象的setValue(key, value(此值是表单Map集合中的value))方法,向对象栈中对象的属性进行赋值.
步骤:
1、 动作类需要实现ModelDriven<Object>个接口
2、 在动作类中定义这个模型(对象属性)的引用,并通过此模型的无参构造创建出此模型的实现对象
3、 重写ModelDriven<Object>个接口接口中的getModel();方法,在此方法中将模型的实例对象返回即可
4、 在表单提交时,表单的name属性只需要写此模型的属性名称即可
示例:(通过模型驱动向动作类中的 对象类型的属性赋值)
JSP页面:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.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">
</head>
<body>
<form action="${pageContext.request.contextPath }/model" method="post">
<!-- 向动作类中的属性注入值 -->
用户名:<input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
<!-- 向动作类中的模型对象中的属性注入值,name的取值必须是模型对象属性的名称 -->
书名:<input type="text" name="bookName"/><br/>
书的单价:<input type="text" name="price"/><br/>
<input type="submit" value="提交"/>
</form>
<s:debug></s:debug>
</body>
</html>
struts.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<!-- struts2中的模型驱动 -->
<struts>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,,do"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<package name="model" extends="struts-default">
<action name="model" class="cn.domain.UserAction" method="execute">
<result name="success" >/index.jsp</result>
</action>
</package>
</struts>
模型(Bean对象):
package cn.domain;
import java.io.Serializable;
//这是一个书的模型(Bean对象)
public class Book implements Serializable {
private String bookName;//书名
private float price;//单价
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
动作类:
package cn.domain;
import java.io.Serializable;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
//模型驱动(实现ModelDriven<Book>接口);其实就是向动作类中的 对象属性 赋值
public class UserAction extends ActionSupport implements Serializable,ModelDriven<Book> {
private String username;
private String password;
private Book book = new Book();//定义模型,并创建出此模型的实例对象
//重写ModelDriven<Book>接口中的getModel方法,并将模型的示例对象返回
@Override
public Book getModel() {
//返回模型(Book)的实例对象,那么 ModelDrivenInterceptor(modelDriven)拦截器会获取到此对象,
//并将此对象压入到ValueStack(值栈对象)的root(对象栈)的栈顶,
//这样便可通过ValueStack对象的setValue(key, value)方法,向对象栈中此book对象的属性进行赋值.
//以上的key和value的取值来自于表单中的name属性和value属性的值(就是请求参数中的那个Map<String,String[]>集合);
return this.book;
}
//动作方法
@Override
public String execute() throws Exception {
//打印动作类中的属性的值
System.out.println("你输入的用户名为:"+this.username);
System.out.println("你输入的密码为:"+this.password);
//打印模型的属性的值
System.out.println("你输入的书名为:"+this.book.getBookName());
System.out.println("你输入的单价为:"+this.book.getPrice());
return Action.SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
}
struts2中常用的标签(或参考struts3.11_tag工程)
如果要使用struts2中的标签,必须导入uri为/struts-tags的标签库,如:<%@ taglib uri="/struts-tags" prefix="s" %>
property标签
property标签用于输出指定值:
<s:set name=“name” value=“kk”/>
<s:property value=“#name”/>
属性描述:
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值
escape:可选属性,指定是否格式化HTML代码
value:可选属性,指定需要输出的属性值(OGNL表达式),如果没有指定该属性,则默认输出ValueStack栈顶的值。
用法:
<!-- property是用于输出指定域中的值(可以用OGNL表达式一起使用)-->
<s:property/><!-- 如果不指定value的值,默认取的是valueStack对象中名称为root的list集合中栈顶的对象 -->
<br/>
<!-- 配合iterator标签遍历List集合 -->
<s:set var="list" value="{'a','b','c'}" scope="page"></s:set><!-- 创建一个list集合 -->
<s:iterator value="#attr.list"><!-- iterator遍历时会将正在遍历的这个对象放在root list集合的栈顶中的值 -->
<s:property/><!-- 如果不指定property标签的value属性那么默认值也是取的root list集合的栈顶中的值 -->
</s:iterator>
<br/>
<!-- 配合iterator标签遍历Map集合 -->
<s:set var="map" value="#{'key1':'value1','key2':'value2','key3':'value3'}" scope="page"></s:set>
<s:iterator value="#attr.map"><!-- 这里遍历出来是一个Entry对象,iterator标签默认将其放在root list集合的栈顶 -->
<s:property value="key"/> : <s:property value="value"/> <!-- 如果没有指定property标签的值,默认到root list集合中去取栈顶的对象,
并取出这个对象中的key属性的值和value属性的值 -->
</s:iterator>
if标签
用于逻辑判断,是否输出其中的内容
属性描述:
test:表达式,判断的逻辑表达式
用法:
<!-- if标签是用于判断OGNL表达式是否成立 -->
<s:set value="'B'" var="score" scope="page"></s:set><!-- 向page域中存入一个值,注意如果要存入的是字符串的话,必须加引号,不然会动作OGNL表达式的变量处理 -->
<s:if test="#attr.score == 'A'">
优秀
</s:if>
<s:if test="#attr.score == 'B'"><!--会在屏幕上显示”一般”-->
一般
</s:if>
<s:if test="#attr.score == 'c'">
不及格
</s:if>
set标签
set标签是向某一个域中设置值(此标签还可以配合OGNL表达式创建List和Map集合,并存储在指定的域中)
属性描述:
value: 是指定存储的值,如果要存储字符串,必须在双引号中将字符串再用单引号存储起来,也可以用OGNL表达式创建一个List或Map集合
scope: 是指定要存储的域,取值范围为:action、page、request、session、apliaction(缺省值为action)
var: 是指定将此值存储在域中时的key
用法:
<!-- set标签是向某一个域中设置值(此标签还可以配合OGNL表达式创建List和Map集合,并存储在指定的域中) -->
<!-- 将一个字符串存储在page域中(注意要存储的字符串必须在双引号中再用单引号引起来,不然会当前一个OGNL表达式) -->
<s:set value="'abc'" var="str" scope="page"></s:set>
<!-- 取出page域中的此字符串 -->
<s:property value="#attr.str"/><br/>
<!-- 创建一个List集合并存储在page域中 -->
<s:set value="{'a','b','c'}" var="li" scope="page"></s:set>
<!-- 取出page与众此list集合中下标为0的值 -->
<s:property value="#attr.li[0]"/><br/>
<!-- 创建一个Map集合并存储在session域中 -->
<s:set value="#{'key1':'value1','key2':'value2','key3':'value3'}" var="map" scope="session"></s:set>
<!-- 取出session域中的此Map集合中 key为"key2"的值 -->
<s:property value="#session.map.key2"/>
iterator标签
iterate标签用于对集合进行迭代,这里的集合包含List、Set、Map和数组
属性描述:
Value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合。
var: 将数据存储在域中时的key,如果没有定定义此属性,那么遍历出来的对象,会被放在root list集合的栈顶.如果定义了此属性,那边被遍历出来的对象,不仅在栈顶有此对象,在action域中也有此对象
status:可选属性,该属性的值引用了迭代时的IterateStatus实例。该实例包含如下几个方法:
int getCount(),返回当前迭代了几个元素。(从1开始)
int getIndex(),返回当前迭代元素的索引。(从0开始)
boolean isEven(),返回当前被迭代元素的索引是否是偶数。
boolean isOdd(),返回当前被迭代元素的索引是否是奇数。
boolean isFirst(),返回当前被迭代元素是否是第一个元素
boolean isLast(),返回当前被迭代元素是否是最后一个元素
begin:开始循环的数,接收类型为int(自定义循环时用)
end: 结束循环的数,接收类型为int(自定义循环时用)
step: 步长,接收类型为int(自定义循环时用)
用法:
<!-- iterator标签主要用于迭代List、Set、Map集合及数组 -->
<!-- 迭代List集合 -->
<s:set value="{'a','b','c','d','e'}" var="list" scope="page"></s:set><!-- 创建一个List集合 -->
<s:iterator value="#attr.list"><!-- iterator标签会包迭代出来的值放在root list集合的栈顶,当前迭代的元素在栈顶 -->
<s:property/><!-- property标签默认取root list集合中栈顶的对象 -->
</s:iterator>
<br/>
<!-- 迭代Map集合 -->
<s:set value="#{'key1':'value1','key2':'value2','key3':'value3'}" var="map1" scope="page"></s:set><!-- 先创建一个Map集合 -->
<s:iterator value="#attr.map1" status="st"><!-- iterator标签会包迭代出来的值放在root list集合的栈顶(这里放的是一个Entry对象) -->
<p>当前是第:<s:property value="#st.count"/>个元素,下标为<s:property value="#st.index"/>
,并且是<s:if test="#st.even">偶数</s:if><s:elseif test="#st.odd">奇数</s:elseif></p><!-- st对象的属性还可以获取是否是最后一个,元素是否是第一个元素(在笔记中的iterator标签中有讲status属性的取值) -->
<s:property value="key"/> : <s:property value="value"/><!-- property标签默认取root list集合中栈顶的对象(这里去的是栈顶对象的key属性和value属性的值) -->
</s:iterator>
<br/><br/><br/>
<!-- 迭代Set集合 -->
<!-- 创建一个Set集合 -->
<%
Set<String> ts = new TreeSet<String>();
ts.add("数据1");
ts.add("数据2");
ts.add("数据3");
ts.add("数据4");
//将此set集合存入到page域中
pageContext.setAttribute("set", ts);
%>
<s:iterator value="#attr.set" var="set"><!-- 如果定义了此var属性,那边被遍历出来的对象,不仅在栈顶有此对象,在action域中也有此对象 -->
<s:property value="set"/>
</s:iterator>
<br/>
<!-- 迭代数组 -->
<!-- 创建一个数组 -->
<%
String[] strr = new String[]{"aa", "bb", "cc", "dd", "ee"};
pageContext.setAttribute("strr", strr);
%>
<s:iterator value="#attr.strr" var="strr"><!-- 直接将数组传入到value属性中即可 -->
<s:property value="strr"/>
</s:iterator>
<br/><br/>
<!-- 自定义for循环 -->
<s:set value="1" var="num" scope="page"></s:set>
<s:iterator begin="1" end="9" step="1"><!-- begin:是起始数,end是结束数,step是步长 -->
当前循环的次数为:<s:property value="#attr.num"/>
<s:set value="#attr.num+1" var="num" scope="page"></s:set>
<br/>
</s:iterator>
<!--更复杂的集合迭代,请参考:struts3.11_tag工程-->
url标签
功能是重写url,此这里的url标签包括了jstl中的url标签中的功能(包括将session的ID写入到此rul后面(前提是禁用cooke)),并且还新增了一个取值功能,和自动通过名称空间和动作名组合成一个新的url.
属性描述:
var:当前的url的值存在域中时的key.如果没有定义此属性,那么重写过后的url值,直接输出到jsp页面上.如果定义了此属性,那么重写过后的url的值就会被存储在 request域和action域(struts2规定死了的) 中,就不会输出在jsp页面上了
namespace:要进行重写的名称空间(以’/’开头)
action:要进行重写的动作名称
value:表示设置要重写的值,也可以在域中去取(用OGNL表达式,别忘了加%{})
子标签:用于以get的方式传递参数
<s:param name="username" value="'zhangsan'"></s:param> name:参数映射关系的key值。value:参数映射关系的value值
用法:
<!-- 功能是重写url,此这里的url标签包括了jstl中的url标签中的功能(包括将session的ID写入到此rul后面(前提是禁用cooke)),
并且还新增了一个取值功能,和自动通过名称空间和动作名组合成一个新的url. -->
<s:url></s:url><!-- 如果不加任何参数,显示出来就是 当前jsp页面的uri(资源地址),如:/struts3.11_tag/tag1.jsp -->
<br/>
<br/>
<s:url namespace="/User" action="user_add"></s:url><!-- 结果为:/struts3.11_tag/User/user_add.action(当前工程名/名称空间/动作名称.action) -->
<br/>
<br/>
<!-- 参数传递 -->
<s:url namespace="/User" action="user_delete">
<s:param name="username" value="'zhangsan'"></s:param>
<s:param name="age" value="'18'"></s:param>
</s:url>
<!-- 结果为:/struts3.11_tag/User/user_delete.action?username=zhangsan&age=18 -->
<br/>
<br/>
<!-- 用OGNL表达式获取到要重写的值 -->
<s:set value="'/User/user_update'" var="urll" scope="page"></s:set>
<s:url value="%{#attr.urll}"></s:url>
<!-- 结果为:/struts3.11_tag/User/user_update -->
<br/>
<br/>
<!-- 把重写后的值不显示在jsp页面,存储在域中,struts2规定存储在request域和action域中 -->
<s:url namespace="/User" action="user_find" var="urlll"></s:url>
<s:property value="#urlll"/><!-- 在action域中找到此值,并输出 -->
filderror标签
在struts2中有两种异常描述的Map集合,这两个Map集合中只要有一条或以上的错误映射关系,那么动作类中的动作方法将不会执行
第一种:fieldErrors异常中的Map集合(字段(属性)错误)
此Map集合是放有映射关系(属性名-异常描述)的异常信息,如果类型转换异常,验证异常。如果需要在JSP页面显示此Map集合中的异常描述信息可以通过<s:fielderror></s:fielderror>标签的方法显
示此Map集合中所有的错误信息,也可以通过<s:fielderror fieldName="属性名(也就是key)"></s:fielderror>的方式获取指定属性的异常描述信息。
actionerror标签
第二种:actionErrors异常中的Map集合(动作(action)错误)
比如在无字段验证时添加的异常信息就会被添加到此Map集合中。如果要在JSP页面显示此Map集合中的异常描述信息,只能通过<s:actionerror/>标签的方式显示此Map中所有的异常描述信息
struts2中的form表单标签
此struts2中的表单标签,并不推荐使用,因为当这些标签被解析成HTML表单时它自动生成了一些其他的标签(如:tr,td,label等标签,和此form表单中只能放表单,不能有其他标签,包括文本字体),并且表单的id等会自动生成,调整样式时不好控制,所以并不推荐使用.
checkboxlist标签(多选按钮标签)
用于根据List集合或Map集合自动生成多选按钮的标签
属性描述:
name:为生成的多选按钮定义的name属性的值
list:传入的值如果是List集合,那么此多选按钮的内容 和 value属性的值一样。如传入的是Map集合,那么此多选按钮中的内容为Map集合value的值,此多选按钮的value为Map集合中key的值(但可以通过listKey属性和listValue属性调整显示的位置)
value:此值接收是一个list集合,传入多选按钮的value属性,那么多选按钮的value值与此集合中的任何一个值匹配,那么它将被选中
listKey:指定多选按钮value的值(正在被迭代的对象的属性)
listValue:指定多选按钮的内容(正在被迭代的对象的属性)
用法:
<!-- name是为这些多选按钮设置一个name属性,list是一个list集合,那么此多选按钮的value值和内容值都是这个list集合中的值,value是指定哪些按钮将被选中 -->
爱好:<s:checkboxlist name="hobby" list="%{{'上网','睡觉','游泳'}}" value="%{{'上网','睡觉'}}"></s:checkboxlist>
<!-- name是为这些多选按钮设置一个name属性,list是一个Map集合,那么此Map集合默认是将Map的key作为多选按钮的value属性值,Map的value作为多选按钮的内容中
但我们也可以用listKey和listValue属性设置Map集合中多选按钮的显示位置 -->
<!-- 当遍历出这个Map集合的Entry对象后,会将这个Entry对象放置在root这个list集合的栈顶,而listKey和listValue取的是栈顶对象的属性值 -->
<s:checkboxlist name="hobby1" list="%{#{1:'上网',2:'睡觉',3:'游泳'}}" value="%{{1,3}}" listKey="%{key}" listValue="%{value}"></s:checkboxlist>
<!-- 传入Bean对象来进行遍历设置多选按钮的value值和内容值 -->
<!-- 创建一个Bean的list集合 -->
<%
ArrayList<Hobby> arr = new ArrayList<Hobby>();
arr.add(new Hobby(100, "K歌"));
arr.add(new Hobby(101, "跳舞"));
arr.add(new Hobby(102, "看书"));
pageContext.setAttribute("arr", arr);
%>
<!-- 当服务器遍历这个list集合时,会将遍历出来的对象放在root这个list集合的栈顶,然后listKye取的是栈顶对象的属性值,和listValue -->
<s:checkboxlist name="hobby2" list="%{#attr.arr}" value="%{{100,102}}" listKey="%{id}" listValue="%{name}"></s:checkboxlist>
radio标签(单选按钮)
select标签(下拉选框)
其他的文本标签和按钮标签和HTML标签使用差不多
token标签(防止表单重复提交)(可以参考struts3.12_form_repeat_submit工程)
属性描述:
此标签没有属性
用法:
1、 只需要将此标签放在<s:form>表单中的任意一个位置即可。
2、 需要在struts.xml中导入一个名为:token的拦截器
3、 再指定一个invalid.token的结果处理器,当发生了重复提交表单后会跳转到此页面
<!--
原理:当我们使用了<s:token>标签时,此标签中会向session中存储一个唯一的字符串,并会在HTML页面中也生成一个隐式标签(hidden),其value的值就是这个唯一的字符串,
当提交时,我们配置的token拦截器就会拦截此请求,并会取出表单中的隐式表单(hidden)的value属性的值(唯一的字符串),和session域中的唯一字符串进行对比较,
如果 隐式表单(hidden)和session域中的唯一字符串相同,那么就放行(执行动作类中的动作方法),并将session域中的唯一字符串清除(防止第二次提交表单是匹配成功)。
如果不一样,就直接返回一个“invalid.token”字符串,所以要指定一个“invalid.token”的结果处理器。(不放行,也就不会执行动作类中的动作方法)
其实这里的原理和JSP中防止表单重复提交的原理是一样的
-->
struts2第三方插件的使用
jFreeChart图形生成
需要导入struts2-jfreechart-plugin-2.1.8.1.jar此jar包是struts2的支持jar包,jfreechart-1.0.13.jar包和此包的依赖包jcommon-1.0.16.jar
请参考struts3.13_jFreeChart工程
struts2中的三个上下文域对象
1、 ActionContext(action域)
此上下文对象在OGNL表达式中已经说过,此凡是存在此域中的数据,都可以被request.getAttribute(“key”);的方式获取到,就是request域中可以获取此action域中的所有值
而此域中又存在其他三个(session,servletContext, valueStack(root list))域的对象,那么就表示request域可以获取到其他所有域中的值了.
获取客户端的语言配置环境
还可以通过此ActionContext对象的getLocale()方法来获取Locale语句环境对象(此对象在java.util包中),通过此Locale对象的toString方法可以获取到当前浏览器的语言环境,还可以通过Locale对象的getDefault()方法获取到java虚拟机的语言配置环境(也就是系统的语言环境).
2、 ServletActionContext
此上下文对象主要是和Servlet交互,此对象中可以获取到Servlet中所有常用的对象
3、 ActionInvocation
此上下文对象是struts2容器的上下文对象,就是struts2中的所有的东西(包括使用的拦截器,和结果处理器对象)都可以通过此对象获取到。
此域的范围也是一次请求范围;
在自定义拦截器和结果处理器中有用到此对象(可以获取到此对象),还可以用ActionContext.getContext().getActionInvocation();的方式获取到此对象。
示例:
//ActionInvocation对象,此对象是struts2上下文对象,可以获取到struts2中用到的所有东西
//获取到ActionInvocation的对象
ActionInvocation ai = ActionContext.getContext().getActionInvocation();
//获取到当前的动作类对象
Object actionObj = ai.getAction();
//获取到ActionContext(action域)上下文件对象
ActionContext ac = ai.getInvocationContext();
//得到Action(动作类)代理对象
ActionProxy ap = ai.getProxy();
//获取到ActionConfig对象
ActionConfig aconf = ap.getConfig();
//获取到所有的拦截器
List<InterceptorMapping> list = aconf.getInterceptors();
//获取到struts.xml中此动作的包名
String packageName = aconf.getPackageName();
//得到当前的动作方法名称,必须在struts.xml配置文件中设置了action的method属性才可以获取到
String methodName = aconf.getMethodName();
//获取到结果处理器的对象(这里取到的为null(可以试一试在 数据返回到客户端时的拦截器中试一试))
Result r = ai.getResult();
//得到这个结果处理器的编码
String code = ai.getResultCode();
//得到ValueStack(值栈对象,内有一个root 的list集合)
ValueStack vs = ai.getStack();
//得到ValueStack值栈对象中的root这个List集合(对象栈)
CompoundRoot root = vs.getRoot();
//得到ActionContext(action域对象)对象,底层维护的Map集合
Map<String,Object> actionMap = vs.getContext();
ObjectFactory对象
此对象是struts2的中的所有对象生产工厂,struts2中的所有对象都是通过此对象进行实例化的(包括拦截器、Action、过滤器、验证器等等)。
可以去参考此对象的方法,都有生产对应对象的方法。
此对象会在spring框架中被替换掉。
student2启动是所做的事情
加载配置文件
当student2初始化时先会加载default.properties文件
然后加载struts-default.xml、struts-plugin.xml、struts.xml这三个配置文件.
如果后面加载的配置文件中设置前面配置文件中的属性(标签),那么将覆盖掉前面配置文件中的属性的值。
这三个文件的DTD约束是一样的,并且此三个文件会按顺序依次加载。
struts-default.xml配置文件
配置及注册结果处理器,默认的拦截器,bean等
struts-plugin.xml配置文件
此文件是一个扩展配置文件,让其他框架可以插件的新式加入到struts中来。
可以有很多个,比如每一个jar包的根目录下都可以有一个此文件。
此文件可以覆盖掉默认的拦截器、结果处理器、bean等。
struts.xml配置文件
配置action的配置文件。
可以通过include标签将其他的xml配置文件引入进来。
struts2对象的创建
当访问一个action时,会被过滤器拦截到,并会执行struts2的过滤器(StrutsPrepareAndExecuteFilter)。
此过滤器会先依次创建以下对象:
创建ValueStack对象, 创建ActionContext对象并将ValueStack值栈对象放入到此ActionContext对象中,并且还将此ActionContext对象放入ThreadLocal对象中(当前线程范围内的共享变量中).
这样ValueStack对象和ActionContext对象都在当前线程中了,也就保证了数据的安全性。
执行executeAction方法
当处理完成以上事情后会执行executeAction方法。
1、 此方法会中创建代理对象。
1. 创建action对象
2. 把此action对象放入到栈顶(通过ValueStack.push(action)方法将此对象放入栈顶);
3. 得到所有的拦截器,并得到此拦截器的迭代器。
2、 执行代理对象的execute方法(执行的是DefaultActionInvaction的execute方法)
1. 执行所有的拦截器
2. 调用action对象中的操作方法
3. 执行结果处理器
3、 当向客户端返回页面以后
1. 清空ActionContext对象中的所有数据