struts 框架
来源:互联网 发布:淘宝网店贷款 编辑:程序博客网 时间:2024/06/04 01:24
1. Struts2 介绍
Struts2是最早的基于mvc模式的应用层框架!在struts1的基础上,融合了webwork框架的功能。即Struts2 = struts1 +webwork。
注:Struts功能的实现是通过核心过滤器实现的。所以如果请求没有经过核心过滤器,则struts的功能是无法使用的。
2. Struts2 开发搭建环境
a) 引入struts2.3必须的jar包(8个)
struts2-core-2.3.4.1.jar
struts2核心包 (org.apache.struts2)
xwork-core-2.3.4.1.jar
webwork核心包(om.opensymphony.xwork2)
commons-fileupload-1.2.2.jar
文件上传功能支持包
commons-io-2.0.1.jar
ognl-3.0.5.jar
ognl表达式 (jsp页面取值)
freemarker-2.3.19.jar
标签模板库
commons-lang3-3.1.jar
struts2对java.lang包的扩展
javassist-3.11.0.GA.jar
strtus2框架对字节码处理的工具包
注:如果少了jar包,启动时就会报错:
Caused by: java.lang.ClassNotFoundException:或,
java.lang.NoClassDefFoundError:。。。(例:org/apache/commons/io/FileUtils)
b) 在web.xml中配置struts核心过滤器
<!-- 配置核心过滤器 (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>
说明:Struts的核心过滤器说白了就是一个普通的过滤器而已,不配置的话,struts的所有功能都无法使用。其中url-pattern是拦截地址,“/*”即所有的请求都进行拦截,这种写法是不推荐的,一般在测试时为了方便才这么写。一般只拦截action,(如配置*.action)
c) 编写Action
/**
* Action类的开发
* 实现接口Action的形式一般不用,而是采用继承ActionSupport的形式,详见Action开发的三种方式:
*/
public class HelloAction implements Action{
@Override
public String execute() throws Exception {
System.out.println("处理请求!!!!");
return "success";
}
}
d) 配置Action
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="hello" extends="struts-default">
<!-- 配置访问路径与action处理类的映射关系 -->
<!-- 访问:http://localhost:8080/struts01/hello -->
<action name="hello" class="cn.itcast.a_hello.HelloAction">
<result name="success">/index.jsp</result>
</action>
</package>
</struts>
package、action、result相关常用配置参数详解:
一、package
name
表示包名称,必须唯一
作用:a. 管理action的; 一般一个模块用一个包;
b. 包的名字,可以被其他包继承
extends
指定当前包继承哪个包;我们写的包,必须继承自struts-default
(在struts-default.xml中有定义包:struts-default)
abstract
设置一个包是否为抽象包,默认为false;
true表示当前包为抽象包,抽象包中不能有action配置,主要是给其他包继承的
namespace
指定当前包的名称空间;
默认为 /
作用:作为访问路径的一部分, 管理路径
即:访问路径 = 名称空间 + ActionName.如:/hello /user
思考: struts为什么要提供名称空间?
当不同的action中有相同的方法时,避免访问路径重复!可以通过名称空间管理!
二、action
name
访问路径名称,与namespace共同组成完成的访问路径
class
访问路径,对应的处理action类
method
当前访问对应action类中的哪个方法,不指定默认为execute
三、 result
name
action中处理请求方法的返回值
type
跳转的结果类型, 默认为转发,即dispatcher
dispatcher: 转发
redirect : 重定向
chain : 转发到action
redirectAction: 重定向到action
下面的这两种方式可以有上面的两种方式替代。
stream : 返回的结果视图对应的是流,可以通过stream指定
(文件下载)
配置的三种方式:
传统配置,
<!--添加-->
<actionname="user"class="cn.itcast.b_config.UserAction"method="add">
<resultname="add"type="dispatcher">/index.jsp</result>
</action>
<!-- 删除 -->
<actionname="delete"class="cn.itcast.b_config.UserAction"method="delete">
<resultname="delete"type="dispatcher">/index.jsp</result>
</action>
<!-- 修改 -->
<actionname="update"class="cn.itcast.b_config.UserAction"method="update">
<resultname="update"type="dispatcher">/index.jsp</result>
</action>
通配符优化,
<!--通配符配置 , 访问: http://localhost:8080/struts01/hello/a/b /user_update-->
<!-- * 的值由运行时期访问时候,传入; {1} 表示第一个*的值; {2} 表示第二个 *的值 -->
<actionname="user_*"class="cn.itcast.b_config.UserAction"method="{1}">
<resultname="{1}">/index.jsp</result>
</action>
动态方法调用
<action name="user" class="..">
</action>
分别这样访问
http://localhost:8080/prj/user!add
http://localhost:8080/prj/user!list
可以执行add()/list()方法
注意:要开启动态方法调用!
3. Struts2 的执行流程
官方的执行流程图:
a) 服务器启动时
加载web.xml;
创建核心过滤器实例
执行过滤器的init()方法,执行初始化,加载下面3个配置文件:
struts-default.xml
路径:struts2-core-2.3.4.1.jar/struts-default.xml
作用:a定义了struts框架在运行时期需要创建的对象,通过bean节点指定。
b指定了默认的包:struts-default。
默认包中指定了结果类型,由result-type结点指定
还有拦截器(32个)相关配置,定义了所有的拦截器和拦截器栈(拦截器栈是为了方便引入拦截器而设计的,相当于文件夹)
C指定了默认的拦截器栈,和默认执行的action类
<default-interceptor-refname="defaultStack"/>
<default-class-refclass="com.opensymphony.xwork2.ActionSupport" />
struts-plugin.xml,
struts2的插件相关配置!
struts.xml
用户编写的struts配置!
配置: 请求的路径与处理的action类之间的映射!
b) 访问时
struts把用户请求,封装为ActionMapper对象
创建配置管理类对:ConfigurationManager。读取struts.xml,创建ActionProxy, 即Action的代理对象。
通过代理对象创建访问的action实例
通过ActionInvocation调用默认拦截器中的18个拦截器(执行顺序从上往下)
执行action中的处理请求的方法
执行完Action中业务处理方法后,又回到拦截器;
最后,由服务器响应用户。
执行流程时序图:
4. Struts2 对域对象的封装
Action中的代码
//3. 保存处理结果 (把数据保存到request/session/application对象中)
// Servlet中: HttpServletRequest/HttpSession/ServletContext对象的方法,把数据保存到域
// struts中, 已经把表示request/session/application对象封装为一个个map集合,
// 我们可以像操作map集合一样操作域对象!
// 获取ActionContext对象
ActionContext ac = ActionContext.getContext();
// 1. 获取表示request域对象的map
Map<String,Object> requestMap = (Map<String, Object>) ac.get("request");
// 2. 获取表示session域对象的map
Map<String,Object> sessionMap = ac.getSession();
// 3. 获取表示application域对象的map
Map<String, Object> applicationMap = ac.getApplication();
// 分别往各个map保存数据 (其实是通过map操作域对象)
requestMap.put("data","request_data1");
sessionMap.put("data","session_data2");
applicationMap.put("data","application_data3");
首页中的代码
<body>
首页<br/>
<br/>1.从request中取数据<br/>
${requestScope.data } //结果为request_data1
<br/>2.从session中取数据<br/>
${sessionScope.data } //结果为session_data2
<br/>3.从application中取数据<br/>
${applicationScope.data } //结果为application_data3
</body>
使用ognl标签可以在struts的值栈中取值,而不用域对象,省去了将值放入域对象中的过程。
5. Action 开发的三种方式:
不继承任何类,也不实现任何接口。
publicclass TestAction1 {}
实现Action 接口
publicclass TestAction2implements Action{}
继承ActionSupport类
publicclassTestAction3 extends ActionSupport{}
注:三种方式都影响struts 功能的使用。但一般情况,我们开发action都是继承ActionSupport。例如:如果要使用struts的数据校验功能,就必须继承ActionSupport类
6. Struts 常量
a) Struts2 修改action的访问后缀
Struts2的默认访问后缀是.action,可以使用struts常量进行修改。
常量定义路径:struts2-core-2.3.4.1.jar/org.apache.struts2/default.properties
常量相关:struts.action.extension=action,,
通过常量修改:
<constant name="struts.action.extension" value="action,do,abc,"></constant>
配置后,当后缀可以为.action .do .abc 或者没有后缀
b) Struts2 常用的常量
指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker 、velocity的输出
<constant name="struts.i18n.encoding" value="UTF-8"/>
自定义后缀修改常量
<constant name="struts.action.extension" value="action"/>
设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭
<constant name="struts.serve.static.browserCache" value="false"/>
当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开
<constant name="struts.configuration.xml.reload" value="true"/>
开发模式下使用,这样可以打印出更详细的错误信息
<constant name="struts.devMode" value="true" />
默认的视图主题,使用struts标签时,不会默认加上很多布局的标签。
<constant name="struts.ui.theme" value="simple" />
与spring集成时,指定由spring负责action对象的创建
<constant name="struts.objectFactory" value="spring" />
该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为 false
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
上传文件的大小限制
<constant name="struts.multipart.maxSize" value=“10701096"/>
c) 动态访问方法调用
<!-- http://localhost:8080/struts02/user!index
! 这个就表示动态方法调用,表示访问的actionName是user,对应的action处理类的index方法执行请求处理
-->
<action name="user"class="cn.itcast.b_constant.UserAction">
<resultname="success">/success.jsp</result>
<resultname="index">/index.jsp</result>
</action>
7. struts 中路径匹配的原则
访问路径 =名称空间 + actionname
<package name="constant" extends="struts-default" namespace="/user">
<action name="user" class="cn.itcast.b_constant.UserAction">
<result name="success">/success.jsp</result>
<result name="index">/index.jsp</result>
</action>
</package>
访问:
http://localhost:8080/struts02/user/user OK
http://localhost:8080/struts02/user/a/b/c/user OK
http://localhost:8080/struts02/user/a/b/user OK
http://localhost:8080/struts02/user/a/user OK
http://localhost:8080/struts02/user/user OK
匹配原则:
http://localhost:8080/struts02/user/a/b/c/user OK
分析:
localhost 本机ip,
8080 tomcat服务
/struts02 项目名称
/user/a/b/c 先搜索有没有这个名称空间,没有,就向钱缩减字符串,
/user/a/b 搜索有没有这个名称空间,没有,继续下一步
/user/a 没有继续下一步
/user 找到后,就不找类,就拿到处理的action类.......
最后一个“/”之后的一定是action name
8. Struts中几个配置文件的加载顺序
启动时候,加载哪些文件:
default.properties
struts-default.xml
struts-plugin.xml
struts.xml (会覆盖default.xml中配置)
struts.properties (会覆盖struts.xml中的配置, 了解,因为会覆盖default.properties中配置) struts会自动加载src/struts.properties
9. 全局结果配置,全局异常配置
a) 全局视图(全局结果)配置
<package name="config" extends="struts-default" namespace="/user">
<!-- 全局视图,给当前包下所有的action用 -->
<global-results>
<result name="success">/success.jsp</result>
</global-results>
<action name="user" class="cn.itcast.c_config.UserAction">
</action>
<action name="test" class="cn.itcast.c_config.TestAction">
</action>
</package>
当多个action有相同的结果集时,可以使用全局结果集配置,注意:只有同一个包中的action才能使用全局配置的结果集。例:
当访问:http://localhost:8080/struts02/user/user
Struts.xml
找名称空间/user
找actionName为user没有配置method属性,再找对应的action类中的execute方法
返回success标记
1.先在当前action子节点找,看有没有配置success标记对应的页面
2.如果没有配置,就找全局视图
3. 全局视图找到,就跳转; 没有找到就报struts提供的错误!
b) 全局异常配置
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEstruts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<packagename="config"extends="struts-default"namespace="/user">
<!-- 配置默认的action类,如果没有配置,使用struts-defalut配置文件中配置的默认类:ActionSupport
<default-action-ref name=""></default-action-ref> -->
<!-- 全局视图,给当前包下所有的action用 -->
<global-results>
<resultname="success">/success.jsp</result>
<resultname="error">/error_null.jsp</result>
</global-results>
<!-- 全局异常配置 -->
<!-- 当当前包下所有的action类中方法,只要出现空指针类型异常,就会跳转到error全局视图对应的错误页面 ! -->
<global-exception-mappings>
<exception-mappingresult="error"exception="java.lang.NullPointerException"></exception-mapping>
</global-exception-mappings>
<actionname="user"class="cn.itcast.c_config.UserAction">
</action>
<actionname="test"class="cn.itcast.c_config.TestAction">
</action>
<!-- 配置的各项目默认值,最简单的action配置 -->
<!-- class :默认处理的类是ActionSupport -->
<!-- method:默认执行execute方法 【execute方法success】 -->
<!-- 必须配置全局视图,配置success视图对应的页面 -->
<actionname="myTest1"></action>
<!--应用: 可以通过myTest访问,跳转到WEB-INF下资源 -->
<actionname="myTest">
<resultname="success">/WEB-INF/index.jsp</result>
</action>
</package>
</struts>
Package结点的配置顺序:
Element :package
ContentModel : (result-types?, interceptors?, default-interceptor-ref?,default-action-ref?, default-class-ref?, global-results?,global-exception-mappings?, action*)
10. Struts 核心业务
a) 数据处理(后台到前台传送数据)
方式一:通过ServletActionContext, 获取原生态的servlet api!
注意:因为要引入原生的servletapi!所以要导入Servlet相关包!Struts与servlet耦合!
publicvoid method1() {
// 方式1:得到原始的HttpServletRequest/HttpSession/ServletContext
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
ServletContext application = ServletActionContext.getServletContext();
// 保存数据
request.setAttribute("data","my_request_data");
session.setAttribute("data","my_session_data");
application.setAttribute("data","my_application_data");
}
方式二:ActionContext 可以获取表示request/session/ServletContext(application)的map集合。(ServletActionContext是ActionContext的子类)。所以我们可以像操作map一样操作域对象!与servlet解耦的方式,操作servlet api!
privatevoid method2() {
// 方式2:先得到ActionContext实例 ,再获取对应的操作域对象的map
ActionContext ac = ActionContext.getContext();
// (1)获取requestMap/sessionMap/applicationMap的方式一
//Map<String,Object> request = (Map<String, Object>) ac.get("request");
//Map<String,Object> session = (Map<String, Object>) ac.get("session");
//Map<String,Object> application = (Map<String, Object>) ac.get("application");
(1)获取requestMap/sessionMap/applicationMap的方式二
Map<String,Object> request = ac.getContextMap();//相当于获取到了request域的map;
Map<String,Object> session = ac.getSession();
Map<String, Object> application = ac.getApplication();
// 设置值
request.put("data","my_request3_data");
session.put("data","my_session3_data");
application.put("data","my_application3_data");
}
方式三:通过接口注入的方式
访问struts的action时,会经过ServletConfig拦截器,会将各种域对象对应的map注入。所以继承接口时,可以获取到相应的map。
publicclassDataAction extends ActionSupport implements RequestAware,
SessionAware, ApplicationAware {
private Map<String, Object>request;
private Map<String, Object>session;
private Map<String, Object>application;
@Override
publicvoid setRequest(Map<String, Object>request) {
this.request =request;
}
@Override
publicvoid setSession(Map<String, Object> session) {
this.session = session;
}
@Override
publicvoid setApplication(Map<String, Object> application) {
this.application = application;
}
@Override
public String execute()throws Exception {
request.put("data","my_request4_data");
session.put("data","my_session4_data");
application.put("data","my_application4_data");
returnSUCCESS;
}
}
如何选择,处理数据方式?
ServletActionContext如果想获取servletapi中定义的更多方法,使用这个类!
ActionContext/接口注入
如果只是往域保存数据,可以用ActionContext;方式3也可以
b) 请求数据自动封装(前台到后台,前台若是使用struts标签,转发到该页面时同样也可以取到值的。)
需要注意的是,无论那种方式变量的名字必须保持一致,且在action中需要定义对应变量的set和get方法。
封装到action的属性变量中
前台代码和后台代码展示:
<body>
<form action="${pageContext.request.contextPath }/user_login.action"method="post">
<table>
<tr>
<td>用户名:</td>
<td>
<inputtype="text"name="username"/>
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<inputtype="password"name="pwd"/>
</td>
</tr>
<tr>
<tdcolspan="2">
<inputtype="submit"value="登陆 ">
</td>
</tr>
</table>
</form>
</body>
publicclassUserAction extends ActionSupport{
private Stringusername;
private Stringpwd;
publicvoid setUsername(String username) {
this.username = username;
}
publicvoid setPwd(String pwd) {
this.pwd = pwd;
}
public String login() {
// 1. 直接拿到封装好的数据
System.out.println(username);
System.out.println(pwd);
/*2.通过request对象获取也可以()
String my_name = ServletActionContext.getRequest().getParameter("username");
System.out.println(my_name);
*/
return"login";
}
}
封装到action的属性对象中
前台代码和后台代码展示:
<tr>
<td>用户名:</td>
<td>
<input type="text"name="user.username"/>
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<inputtype="password"name="user.pwd"/>
</td>
</tr>
publicclassUserAction extends ActionSupport{
// 封装请求数据 (对象,一定要给get/set方法)
private Useruser;
publicvoid setUser(User user) {
this.user = user;
}
public User getUser() {
returnuser;
}
请求数据的自动封装,由struts的拦截器: params完成。
<interceptorname="params"class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
11. Struts的拦截器
² 概念
框架之所以能提高开发效率是因为已经实现了一些常用的功能,是软件的半成品。Struts同样也是,struts预先实现的功能都是通过一个个的拦截器实现的。一个拦截器代表一个功能,用户可以根据自己的需要,自有组装。
拦截器栈
为了方便对拦截器的引用,strtus允许定义拦截器栈, 一个拦截器栈中可以包含多个拦截器,栈中拦截器的顺序就是执行的顺序。拦截器栈中也可以引用另外的栈。如果用户没有指定使用哪些拦截器,也就是说没有制定使用哪个拦截器栈, struts默认在创建action之后,自动调用默认栈, 即”defaultStack”, 这个栈里面定义了默认执行的18个拦截器
自定义拦截器
当struts提供的拦截器满足不了我们的需求时,用户可以自定义拦截器,如果自定了拦截器,执行的时候,默认的栈一定要执行!(否则不能使用struts的功能,因为struts的功能是通过拦截器实现的)
拦截器就是用来拦截Action请求的!
拦截器的使用
写一个拦截器类,实现interceptor接口
publicclassHelloInterceptor implements Interceptor{
// 服务器启动时候创建单例实例
publicHelloInterceptor() {
System.out.println("---> 1.创建拦截器实例");
}
// 服务器启动时候,创建完对象后执行
publicvoid init() {
System.out.println("---> 2.执行拦截器init()初始化方法");
}
@Override
public String intercept(ActionInvocation invocation)throws Exception {
System.out.println("---> 4.执行拦截业务开始...........");
// 获取当前运行的action
Object action = invocation.getAction();
// 得到代理对象
ActionProxy proxy = invocation.getProxy();
String actionName = proxy.getActionName();//配置的action的name属性值
String namespace = proxy.getNamespace();//package中的名称空间的值
String method = proxy.getMethod();调用的action方法
// 放行 (进入下一个拦截器或action的业务方法)
// invoke()返回值为action中业务方法返回值
// 注意: 一旦执行了Actin的业务方法,返回的结果视图已经确定,再修改返回值没有意义,依然会跳转到action的返回结果对应的页面
String resultFlag = invocation.invoke(); // login
System.out.println("---> 6.执行action业务方法结束, 又回到拦截器");
return"error";
}
@Override
publicvoid destroy() {
System.out.println("---> 7.销毁");
}
}
Action代码
public class UserAction extends ActionSupport{
public UserAction(){
System.out.println("--->3. UserAction实例创建.......");
}
public String login() {
System.out.println("---->5. UserAction.login()");
return "login";
}
}
序号是访问action时的执行顺序
在struts配置文件中,配置拦截器(只有该package中的action可以使用这个自定义的拦截器)
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPE strutsPUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="interceptor_"extends="struts-default">
<!-- 1. 拦截器配置 (拦截当前包下所有的action) -->
<interceptors>
<!-- 1.1 自定义拦截器 -->
<interceptor name="helloInterceptor" class="cn.itcast.f_interceptor.HelloInterceptor"></interceptor>
<!-- 1.2自定义拦截器栈 -->
<interceptor-stack name="myStack">
<!-- 1.2.1 引入默认栈 (必须引入、且放在第一行) -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 1.2.2 引入自定义的拦截器 -->
<interceptor-ref name="helloInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 2. 执行拦截器 -->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
<action name="user_*"class="cn.itcast.f_interceptor.UserAction"method="{1}">
<resultname="login">/index.jsp</result>
</action>
</package>
</struts>
² 执行流程(时序图)
拦截器执行流程:
1.先执行Action创建
2.再执行拦截器
3.如果拦截器放行,即invocation.invoke(),进入下一个拦截器或者action方法
4.action方法执行完,又回到拦截器。
拦截器如何调用:
过滤器调用拦截器!
12. Struts 标签和ognl表达式
Struts中必须引入ognl.jar,即默认对ognl表达式语言的支持,其作用就是在jsp页面中取值。
Ognl表达式语言(了解)
Ognl表达式取值和EL表达式取值的区别
EL表达式时jsp页面取值的标准;
Ognl表达式时struts2标签取值的标准,必须配合struts标签使用
Struts标签能在jsp页面取到值的原因:Struts运行数据都存储到值栈中,值栈对象又存到request域对象中;转发到的页面就可以拿到request中值栈对象,从而可以使用struts标签从值栈中取值!
Ognl表达式简介
OGNL是Object GraphicNavigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
OGNL优势
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或@tutorial.MyConstant@APP_NAME;
3、支持赋值操作和表达式串联,如price=100, discount=0.8,
calculatePrice(),这个表达式会返回80;
4、访问OGNL上下文(OGNL context)和ActionContext;
5、操作集合对象。
OGNL有一个上下文(Context)概念,说白了就是一个MAP结构,它实现了java.utils.Map的接口。
OgnlContext对象:Ognl表达式语言的核心对象。
* 总结:<s:property value="#xxx"/>
*
* 1. ognl表达式语言,取根元素的值,直接写变量名
* 2. 如果取非根元素的值,还要带上“#”号
* 3. 通常,在开发中,不需要通过硬编码的方式使用Ognl表达式,即OgnlContext对象
* 只是在struts标签取值中用到OgnlContext这个对象取值,但标签封装了整个取值过程
// 测试:ognl表达式语言的核心对象: OgnlContext对象用法
publicclass OgnlContextTest {
@Test
publicvoid testApp1()throws Exception {
// 创建一个OgnlContext对象
// 是一个map接口: public class OgnlContext extends Object implements Map
OgnlContext context = new OgnlContext();
// ----->保存值 (当成map用)
context.put("cn","China");
// 获取值
String value = (String) context.get("cn");
//------->往根元素中设置值
User user = new User ();
user.setId(1000);
user.setName("Jackcy");
context.setRoot(user); // 根元素
// 获取根元素中值
Object ognl = Ognl.parseExpression("name");// 得到一个Ognl表达式对象
Object value_ = Ognl.getValue(ognl, context, context.getRoot()); // 解析Ognl表达式
System.out.println(value_);
}
//-------> 往非根元素中设置值
@Test
publicvoid testApp2()throws Exception {
OgnlContext context = new OgnlContext();
User user = new User ();
user.setId(1000);
user.setName("Jackcy");
// 设置值到非根元素中
context.put("user", user);
/*******获取非根元素值*******/
// 先得到ognl表达式
Object ognl = Ognl.parseExpression("#user.name");
// 解析表达式获取值
Object value = Ognl.getValue(ognl, context,context.getRoot());
// 测试:是否解析表达式获取值成功
System.out.println(value);
}
}
ValueStack值栈对象
ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。
值栈对象,即ValueStack对象,贯穿整个Action生命周期!当用户访问Action的时候,会创建一个Action对象、一个ValueStack对象、一个ActionContext对象。然后把Action对象放到值栈中(值栈即struts中数据存储中转站),还可以通过ActionContext对象获取值栈(ActionContext对象给用户使用)。最后,把值栈放到request对象。那么页面可以拿到request中值栈的数据!就可以使用struts2运行时期产生的数据!
ValueStack 存储结构
Struts中对域对象的重新封装
parameters: 该 Map 中包含当前请求的请求参数
request: 该 Map 中包含当前 request 对象中的所有属性
session: 该 Map 中包含当前 session 对象中的所有属性
application:该 Map 中包含当前 application 对象中的所有属性
attr: 该 Map 按如下顺序来检索某个属性: request, session,application
获取值栈对象的两种方式
方式一:从request对象获取
HttpServletRequest request = ServletActionContext.getRequest();
ValueStack vs1 = (ValueStack) request.getAttribute("struts.valueStack");
方式二:通过ActionContext获取值栈对象
ValueStack vs = ActionContext.getContext().getValueStack();
操作值栈中的元素
操作根元素集合(list集合)
ValueStack vs = ActionContext.getContext().getValueStack();
/*
* 1. 操作根元素数据,有哪些方式?
*/
vs.pop(); // 移除栈顶元素
vs.push(myuser); // 入栈
// Map集合存储数据到list集合的栈顶元素中
vs.set("cn","China");
vs.set("usa","America");
操作非根元素
// 操作值栈中非根元素值: 存储映射数据
public String execute()throws Exception {
ActionContext ac = ActionContext.getContext();
// 通过ActionContext获取
ValueStack vs = ac.getValueStack();
// 映射数据,存储到非根对象中 (OgnlContext 维护的 Map集合中)
Map<String,Object> request = (Map<String, Object>) ac.get("request");
Map<String,Object> session = ac.getSession();
Map<String,Object> application = ac.getApplication();
// 设置数据
request.put("request_data","request_data");
session.put("session_data","session_data");
application.put("application_data","application_data");
System.out.println(vs);
returnsuper.execute();
}
Struts标签和页面取值
后台action代码
//测试值栈数据是否传递到jsp
publicclassOgnlAction extends ActionSupport {
// 根元素数据
private Useruser = new User();
public User getUser() {
returnuser;
}
publicvoid setUser(User user) {
this.user = user;
}
@Override
public String execute()throws Exception {
// 先获取ActionContext对象
ActionContext ac = ActionContext.getContext();
ac.getValueStack();
// 非根元素数据
Map<String,Object> request = (Map<String, Object>) ac.get("request");
Map<String,Object> session = ac.getSession();
Map<String,Object> application = ac.getApplication();
// 域对象中设置数据
request.put("request_data","request_data");
session.put("session_data","session_data");
application.put("application_data","application_data");
List<User> listUser = new ArrayList<User>();
/**
* list集合,保存在request
*/
for (int i=1; i<11; i++) {
User user = new User();
user.setId(i);
user.setName("Tom" + i);
listUser.add(user);
}
/**
* map集合,保存在request
*/
Map<Integer,User> mapUser = new HashMap<Integer,User>();
for (int i=1; i<11; i++) {
User user = new User();
user.setId(i);
user.setName("Tom" + i);
mapUser.put(user.getId(), user);
}
// 保存
request.put("listUser", listUser);
request.put("mapUser", mapUser);
returnsuper.execute();
}
}
前台jsp页面代码
<%@ page language="java"import="java.util.*"pageEncoding="UTF-8"%>
<!-- 引入struts核心标签库 -->
<%@taglib uri="/struts-tags"prefix="s"%>
<!DOCTYPE HTMLPUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>页面取值</title>
<meta http-equiv="pragma"content="no-cache">
<meta http-equiv="cache-control"content="no-cache">
<meta http-equiv="expires"content="0">
<style type="text/css">
.odd{background-color: red; }
.even{background-color:blue;}
</style>
</head>
<body>
<!-- 一、EL表达式取值 -->
${user.id }
${user.name }
${request_data }
${session_data }
${application_data } <hr/>
<!-- 二、用Strus标签取值 (用# 不用#)-->
<!-- 1. 取值标签 -->
<!-- 1.1 根元素数据 -->
<s:propertyvalue="user.id"/>
<s:propertyvalue="user.name"/>
<!-- 1.2 非根元素取值 -->
<s:propertyvalue="#request.request_data"/>
<s:propertyvalue="#session.session_data"/>
<s:propertyvalue="#application.application_data"/>
<!-- 2. 迭代标签 -->
<br/>2.1迭代list<br/><br/>
<s:iteratorvar="user"value="#request.listUser"begin="2"end="9"status="st">
<s:propertyvalue="#st.even"/>
<s:propertyvalue="#user.id"/>
<s:propertyvalue="#user.name"/> <br/>
</s:iterator>
<br/>2.2迭代map (奇偶行变色)<br/><br/>
<table>
<tr>
<td>序号</td>
<td>编号</td>
<td>名称</td>
</tr>
<s:iteratorvar="en"value="#request.mapUser"status="st">
<trclass=<s:propertyvalue="#st.even?'even':'odd'"/> >
<td><s:propertyvalue="#st.count"/></td>
<td><s:propertyvalue="#en.key"/></td>
<td><s:propertyvalue="#en.value.name"/></td>
</tr>
</s:iterator>
</table>
<br/>3.文本框标签(UI, user interface 标签的其中一个)<br/>
<s:formaction="/ognl.action"method="post">
用户名: <s:textfieldname="userName"></s:textfield>
</s:form>
<!--
4. struts标签取值中几个特殊符号:
% 提供一个ognl表达式运行环境
$ xml 取值使用
# 取非根元素数据
-->
<!-- 文本框标签: 从request中取值赋值给文本框标签! -->
<s:textfield name="user_name" value="%{#request.request_data}"></s:textfield>
<!-- 默认不支持ognl表达式,所以会原样输出文本框的值为“address” -->
<s:textfield name="user_address" value="address"></s:textfield>
<!-- 会从根元素取值,为"",因为默认支持ognl表达式 -->
<s:property value="address"/>
<!-- 原样输出字符串: 'address'-->
<s:property value="'address'"/>
<!-- struts提供类一个调试标签,可以查看值栈结构
<s:debug></s:debug> -->
</body>
</html>
13. struts标签取值中几个特殊符号:
% 提供一个ognl表达式运行环境
$ xml 取值使用,可以使用jsp页面的方式取值。
# 取非根元素数据
具体参考上面的jsp页面代码。
14. Struts2 的文件上传与下载
a) 文件上传(servlet采用FileUpload组件)
只要表单符合规则,在action中可以直接拿到上传的文件。再根据需要处理。文件上传功能由文件上传拦截器完成:
<interceptor name="fileUpload"
class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
文件上传表单
<form action="${pageContext.request.contextPath }/upload.action"method="post"enctype="multipart/form-data">
用户:<input type="text"name="userName"/> <br/>
文件:<input type="file"name="file1"> <br/>
<inputtype="submit"value="文件上传"/>
</form>
后台action对文件处理
publicclassUploadAction extends ActionSupport{
// 拿到请求数据: 普通数据、文件
private StringuserName;
publicvoid setUserName(String userName) {
this.userName = userName;
}
// 获取jsp上传的文件对象
private Filefile1;
// 获取上传的文件名
private Stringfile1FileName;
// 获取上传的文件类型(web.xml识别的文件类型)
private Stringfile1ContentType;
publicvoid setFile1(File file1) {
this.file1 = file1;
}
publicvoid setFile1FileName(String file1FileName) {
this.file1FileName = file1FileName;
}
publicvoid setFile1ContentType(String file1ContentType) {
this.file1ContentType = file1ContentType;
}
// 处理文件上传
public String execute()throws Exception {
//-- 上传到服务器项目目录下的upload目录
// 获取upload目录的绝对路径
ServletContext sc = ServletActionContext.getServletContext();
String basePath = sc.getRealPath("/upload");
// 把上传的文件(file1), 上传到上面的目录下
FileUtils.copyFile(file1,new File(basePath,file1FileName));
returnSUCCESS;
}
}
注意:文件上传,提交地址不要写“upload.action”!可能会跟struts中的action冲突。 换个名称!
文件上传细节:
超出文件大小时会报错org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException:the request was rejected because its size (24513033) exceeds the configuredmaximum (2097152) 2M
默认文件上传大小为2M 即2*1024*1024=2097152。可以进行设置文件上传大小: 在struts中配置常量
<constant name="struts.multipart.maxSize" value="31457280"></constant>
这些常量可以在default.properties /struts.multipart.maxSize=2097152中查看
限制文件上传类型
可以在jsp中用js校验;
也可以在struts配置文件中,给文件上传拦截器注入参数
<actionname="upload_"class="cn.itcast.b_upload.UploadAction">
<!-- 如果指定执行拦截器,默认拦截器不会执行-->
<interceptor-refname="defaultStack">
<!--指定运行上传的文件的类型
<param name="fileUpload.allowedTypes">text/plain</param>
-->
<!--允许上传的文件的后缀名(与上面配置取交集) -->
<paramname="fileUpload.allowedExtensions">txt,jar</param>
</interceptor-ref>
<!-- 文件上传成功 -->
<resultname="success">/b/success.jsp</result>
<!-- 文件上传失败,struts会取找input 错误视图 -->
<resultname="input">/b/error.jsp</result>
</action>
注意:不能在action代码中判断,因为判断时文件已经上传成功,只不过是临时文件而已。
文件上传失败的错误处理
truts文件上传失败,会自动找input错误视图!可以在struts配置文件中配置:
<!-- 文件上传失败,struts会取找input错误视图 -->
<result name="input">/b/error.jsp</result>
error.jsp
<!-- 显示struts框架运行时候产生的所有错误! -->
<s:fielderror></s:fielderror>
b) 文件下载(比较麻烦,可以采用servlet方式)
Servlet方式:a. 先拿到response对象;b. 设置相应文件流数据
c. 设置响应头
struts方式:返回的是输入流,读取文件。配置文件中,应用结果集
<result-type name="stream"class="org.apache.struts2.dispatcher.StreamResult"/>
代码示例:
List.jsp 文件下载列表
<body>
<table>
<tr>
<td>序号</td>
<td>文件名</td>
<td>操作</td>
</tr>
<s:iterator var="fileName"value="#request.fileNames"status="st">
<tr>
<td><s:property value="#st.count"/></td>
<td><s:property value="#fileName"/></td>
<td>
<s:a href="down_down.action?fileName=%{#fileName}">下载</s:a>
</td>
</tr>
</s:iterator>
</table>
</body>
DownAction.java
/**
* 文件下载Action
*/
publicclassDownAction extends ActionSupport{
// 一、列表展示方法
public String list()throws Exception {
// 获取目录路径
String bathPath = ServletActionContext.getServletContext().getRealPath("/upload");
// 目录对象
File file = new File(bathPath);
// 获取目录下的所有文件名
String fileNames[] = file.list();
// 保存到request
ActionContext.getContext().getContextMap().put("fileNames", fileNames);
return "list";
}
// 二、文件下载调用的方法
publicString down() throws Exception {
return"down";
}
// 1. 获取当前下载的文件名
private StringfileName;//文件名跟前台一致,struts会自动注入的。
publicvoid setFileName(String fileName) {
// 对传入的下载的文件名处理get中文问题 (超链接get提交),对中文进行编码处理。
try {
fileName = new String(fileName.getBytes("ISO8859-1"),"UTF-8");
this.fileName = fileName;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// 2. action类中返回文件流的方法 【strut.xml中调用, <param name="inputName">myFileStream</param>】
public InputStream getMyFileStream(){
return ServletActionContext.getServletContext().getResourceAsStream("/upload/" +this.fileName);
}
//3. 下载显示的文件名 【struts.xml中, <param name="contentDisposition">attachment;filename=${downFileName}</param>】
可以看出配置文件中取值,并不是根据action中的变量取值,而是根据get方法
public String getDownFileName() {
// 对处理好的文件名,再进行url编码,浏览器会自动解码显示正确的中文数据
try {
fileName = URLEncoder.encode(fileName,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 返回编码后字符串
returnfileName;
}
}
Struts.xml
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<packagename="upload"extends="struts-default">
<actionname="upload_"class="cn.itcast.b_upload.UploadAction">
<!--如果指定执行拦截器,默认拦截器不会执行-->
<interceptor-refname="defaultStack">
<!-- 指定运行上传的文件的类型
<param name="fileUpload.allowedTypes">text/plain</param>
-->
<!-- 允许上传的文件的后缀名(与上面配置取交集) -->
<paramname="fileUpload.allowedExtensions">txt,jar</param>
</interceptor-ref>
<!-- 文件上传成功 -->
<resultname="success">/b/success.jsp</result>
<!-- 文件上传失败,struts会取找input错误视图 -->
<resultname="input">/b/error.jsp</result>
</action>
<!-- 文件下载 -->
<actionname="down_*"class="cn.itcast.b_upload.DownAction"method="{1}">
<!-- 列表展示 -->
<resultname="list">/b/list.jsp</result>
<!-- 文件下载标记,对应的是流 -->
<resultname="down"type="stream">
<!-- 指定下载的文件的类型:为二进制类型(tomcat服务器找) -->
<paramname="contentType">application/octet-stream</param>
<!-- action类中返回文件流的方法 -->
<paramname="inputName">myFileStream</param>
<!-- 下载显示的文件名 -->
<paramname="contentDisposition">attachment;filename=${downFileName}</param>
<!-- 读取缓存区大小 -->
<paramname="bufferSize">1024</param>
</result>
</action>
</package>
</struts>
15. Struts2 数据校验
数据校验分为前台校验和后台校验。
前台校验也称之为客户端校验,主要是通过JavaScript编程的方式进行表单数据的验证。特点:发生在客户端浏览器,效率高!同时不安全!
后台校验 也称之为服务器端校验,通过服务器代码进行验证。
特点:增大服务器运行压力,效率相对低!但安全、可靠!
对于两种校验方式,一般采用前台验证相对较多;有安全性要求的系统,一般前台验证 + 后台验证!
Struts2 采用的都是后台验证的方式。
a) Action中代码方式
前台的form表单:
<form action="${pageContext.request.contextPath}/login.action"method="post">
用户名:<inputtype="text"name="user.userName"/> <br/>
密码:<inputtype="password"name="user.pwd"/> <br/>
<input type="submit"value="登录"/>
</form>
Action中应写方法validate,方法名不能改变。但这种写法无论访问action中的哪个方法都会执行验证方法。如果对特定方法(例“login”)进行验证时,应写validate+方法名(例:validateLogin)
//2. 数据验证
@Override
publicvoid validate() {
if (user.getUserName() ==null || "".equals(user.getUserName())){
// 添加错误信息,key对应jsp页面中的fieldName,如:
<s:fielderror fieldName="userName"></s:fielderror>
super.addFieldError("userName","用户名不能为空!");
}
if (user.getPwd() ==null ||"".equals(user.getPwd())){
super.addFieldError("pwd","密码不能为空!");
}
}
http://localhost:8080/struts04/login
对于上述访问地址,验证方法执行通过后,才会执行访问的action中的execute方法;验证失败会去找结果集为“input”的视图(见验证失败处理:),即<result name=”input”>error.jsp</result>
验证的原理:
父类中的addFieldError方法,采用map的形式存储错误信息,可以错误信息的代号,value就是错误信息。对于代号相同的错误信息,会放到一个list中
publicsynchronizedvoid addFieldError(String fieldName, String errorMessage) {
// 保存的错误信息的集合
final Map<String, List<String>> errors = internalGetFieldErrors();
// 错误信息集合的Map
List<String> thisFieldErrors = errors.get(fieldName);
if (thisFieldErrors == null) {
//根据代号,获取错误信息的list,
//如果是一个新的错误信息代号,则对该错误信息代号对应的错误信息的list实例化
thisFieldErrors = new ArrayList<String>();
// 先把错误信息的list,放到map集合中
errors.put(fieldName, thisFieldErrors);
}
// list中设置错误信息
thisFieldErrors.add(errorMessage);
}
验证失败处理:
如果数据效验失败,struts2框架会去相应action的配置文件中,找结果为input的结果接对应的页面,如果没有配置,则:
HTTP Status 404 –
No result defined foraction cn.itcast.a_validate.LoginAction and result input
解决方案:
<!-- 验证失败,配置input错误视图,对应登陆页面 login.jsp -->
<resultname="input">/login.jsp</result>
页面用struts标签显示错误
<!-- 1. 显示struts运行时候产生的所有错误信息
<s:fielderror></s:fielderror>
-->这种方式,会显示struts运行过程中,所有的错误信息。
或者,指定显示哪些错误:
<form action="${pageContext.request.contextPath }/login.action"method="post">
用户名:<inputtype="text"name="user.userName"/>
<s:fielderror fieldName="userName"></s:fielderror>
<br/>
密码:<inputtype="password"name="user.pwd"/>
<s:fielderror fieldName="pwd"></s:fielderror>
<br/>
<input type="submit"value="登录"/>
</form>
Struts2 显示错误标签默认使用html中的列表标签(<ul><li>) ,这样会自动换行显示列表,这样极其影响用户体验。解决方案有两种:
可以在jsp页面中,加上标签样式。
<!-- 设置样式,修改struts的s:fielderror标签默认的样式 -->
<styletype="text/css">
ul{
display: inline;
}
ul li{
display: inline;
color: red;
}
</style>
项目中添加资源文件
项目src\ template\simple\ fielderror.ftl。这里的fielderror.ftl为标签定义的模板文件,是已经修改好的(只显示错误信息,没有html标签),会覆盖jar包自带的文件!
b) 配置文件xml的形式
当在action中需要验证很多字段,验证逻辑都相似、或是常用的验证逻辑,这个时候可以使用struts提供的xml配置的方式进行验证。
Struts提供的所有验证器定义路径:
xwork-core-2.3.4.1.jar/com.opensymphony.xwork2.validator.validators/default.xml
xml配置的写法,可以参考约束文件xwork-core-2.3.4.1.jar/xwork-validator-1.0.3.dtd
约束文件:xwork-validator-1.0.3.dtd
<?xml version="1.0" encoding="UTF-8"?>
<!--
XWork Validators DTD.
Used the following DOCTYPE.
<!-- xml文件头的编写 -->
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
-->
<!—- 根元素为validators且子元素应为field或者validator(两者只能出现一个),并出现一次或多次 -->
<!ELEMENT validators (field|validator)+>
<!—field标签的子标签是field-validator出现一次或多次,且还必须要有name属性 -->
<!ELEMENT field (field-validator+)>
<!ATTLIST field
name CDATA #REQUIRED
>
<!-- field-validator标签中的子标签有message标签且只能出现一次,param标签可以不出现或者出现多次 -->
<!ELEMENT field-validator (param*, message)>
<!-- field-validator 标签中必须要有type属性 -->
<!ATTLIST field-validator
type CDATA #REQUIRED
short-circuit (true|false) "false"
>
<!ELEMENT validator (param*, message)>
<!ATTLIST validator
type CDATA #REQUIRED
short-circuit (true|false) "false"
>
<!ELEMENT param (#PCDATA)>
<!ATTLIST param
name CDATA #REQUIRED
>
<!ELEMENT message (#PCDATA|param)*>
<!ATTLIST message
key CDATA #IMPLIED
>
Xml配置文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEvalidators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<!-- 一个field对应要验证的一个字段 -->
<fieldname="user.userName">
<field-validatortype="requiredstring">
<message>用户名不能为空!</message>
</field-validator>
</field>
<fieldname="user.pwd">
<field-validatortype="requiredstring">
<message>密码不能为空!</message>
</field-validator>
<field-validatortype="stringlength">
<!—此处参数的name的值应在验证器
xwork-core-2.3.4.1.jar/com.opensymphony.xwork2.validator.validators/default.xml对应的类中查找(定义的变量) -->
<paramname="minLength">3</param>
<paramname="maxLength">6</param>
<message>密码必须是3-6位!</message>
</field-validator>
</field>
</validators>
注:XML验证文件,必须与Action类所在同一个包中!
xml 文件的命名规则:
语法: ActionClassName-validation.xml
举例:LoginAction-validation.xml
结果:这个文件就会验证LoginAction中所有的方法,如果是验证指定的方法,命名规则如下:
语法: ActionClassName-ActionName-validation.xml.ActionName是指访问action时的名称。
举例: LoginAction- user_login-validation.xml
16. Struts中实现国际化
写资源文件的语法格式:基础名_语言简称_国家简称.properties。例:msg_zh_ch.properties.
Servlet中加载资源文件,可以用ResourceBundle工具类;jsp页面中,使用国际化和格式化标签,也就是fmt相关标签。
框架中国际化的实现首先需要通过常量配置加载资源文件(只需要指定资源文件的基础名即可)
<!-- 通过常量加载资源文件 -->
<constantname="struts.custom.i18n.resources"value="cn.itcast.b_i18n.msg"></constant>
<!—其中pwd就是配置文件中的key -->
Jsp页面中使用struts标签<s:text name="pwd"></s:text>
17. Struts2 的类型转换器
对于基本的数据类型,struts会自动转换。但对于日期类型默认只支持一种格式:yyyy-MM-dd。对于其他的日期格式以及自定义的类型,可以通过定义struts的类型转换器来实现。如果类型转换失败,struts会依然会在配置文件的结果集中寻找结果为input的结果。
自定义类型转换器的结构:
|-- TypeConverter
|--DefaultTypeConverter 默认的类型转换器
|--StrutsTypeConverter 用户自定义类型转换器,继承这个类即可。
a) 局部类型转换器
写类型转换器类
自定义类型转换器(以日期转换器为例),继承类StrutsTypeConverter。重写方法public Object convertFromString
publicclassDateConverter extends StrutsTypeConverter{
/*
* 转换器支持:多种日志格式的转换,yyyy-MM-dd
* yyyyMMDD yyyy年MM月dd日 yyyy/MM/dd ......
*/
// 预定义支持的转换格式
private DateFormatdateFormat[] = {
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyyMMDD"),
new SimpleDateFormat("yyyy/MM/dd"),
new SimpleDateFormat("yyyy年MM月dd日")
};
/**
* 把表单提交的字符串数据,转换为指定的目标类型
* @param context 上下文map集合
* @param values 表单数据
* @param toClass 要转换的目标类型
*
*/
public Object convertFromString(Map context, String[] values,Class toClass) { // 非空判断
if (values ==null || values.length == 0) {
returnnull;
}
// 类型判断
if (toClass != Date.class) {
returnnull;
}
// 遍历转换
for (int i=0; i<dateFormat.length; i++) {
try {
returndateFormat[i].parse(values[0]);
} catch (ParseException e) {
continue; // 继续向下转换
}
}
returnnull;
}
public String convertToString(Map context, Object o) {
returnnull;
}
}
配置类型转换器(通知struts,让它自动调用转换器)
规则: ActionClassName-conversion.properties
举例: ResigterAction-conversion.properties
注意: Action类与配置文件在同一个包,只会对特定的action起作用。
ResigterAction-conversion.properties 的内容:
user.birth=cn.itcast.c_converter.DateConverter
b) 全局类型转换器
也是分两步:但写转换器的步骤与局部转换器完全一样,配置略有不同。
规则: src/ xwork-conversion.properties
配置文件的内容:java.util.Date=cn.itcast.c_converter.DateConverter
cn.itcast.c_converter.Address=cn.itcast.c_converter.AddressConverter
需要转换为达特类型时,就会去调用cn.itcast.c_converter.DateConverter转换器。Address是自定义的类型。
18. Struts的模型驱动
对于struts提供的数据封装已经知道有两种方式了,一是把数据封装到action的属性变量中,二是将数据封装到action的属性对象中(详见数据自动封装)。对于模型驱动可以将两种方式进行整合,即前台执行属性变量的名字(例:userName),而不用写(例user.userName)的形式,后台就可以封装到对象中。实现步骤如下:
实现模型驱动的接口;
对要进行封装的对象进行实例化;
实现接口中的方法(getModel)
前台jsp页面代码:
<form action="${pageContext.request.contextPath }/login.action"method="post">
用户名:<input type="text"name="userName"/> <br/>
密码:<input type="password"name="pwd"/> <br/>
<inputtype="submit"value="注册"/>
</form>
后台action的代码
//1. 实现模型驱动接口
publicclassUserAction extends ActionSupportimplements ModelDriven<User>{
//2. 要封装的对象一定要实例化
private Useruser = new User();
public User getUser() {
returnuser;
}
// 3. 重写模型驱动接口方法, 返回实例化后的对象
@Override
public User getModel() {
returnuser;
}
// 处理前台请求的方法
@Override
public String execute()throws Exception {
System.out.println(user);
returnsuper.execute();
}
}
原理讲解:
模型驱动依然是靠拦截器实现的:模型驱动拦截器!
<interceptorname="modelDriven"
class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
拦截器部分源码:
public String intercept(ActionInvocation invocation)throws Exception {
//获取请求的action
Object action = invocation.getAction();
if (action instanceof ModelDriven) {
ModelDriven modelDriven = (ModelDriven) action;
//获取值栈对象
ValueStack stack = invocation.getStack();
//从重新的方法中获取需要封装的对象
Object model = modelDriven.getModel();
//因为已经对要封装的对象进行了实例化,所以对象不空。
//对象为空,则模型驱动不能自动封装(因为此if,没有else)
if (model != null) {
//将对象放入值栈,
//这样对象的属性也在值栈中,就可将变量的值通过变量名放到对象相应的属性中,
//从而事项了两种方式的结合。
stack.push(model);
}
if (refreshModelBeforeResult) {
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
}
- Struts框架
- Struts框架
- Struts框架
- struts框架
- Struts框架
- Struts框架
- Struts框架
- Struts框架
- Struts框架
- Struts框架
- Struts框架
- Struts框架
- Struts框架
- struts 框架
- struts框架
- Struts 框架
- struts框架
- struts框架
- 关于jdk的报错问题 It is indirectly referenced from required .class files
- 火狐浏览器安装firebug失败的解决方法
- 线段树模板
- 关于C语言数组利用指针排序的问题
- nm 目标文件格式分析
- struts 框架
- Unity获取游戏对象详解
- C++Primer 练习9.44 解答
- zoj 2100 Seeding
- cocos2dx 关于lua 绑定的环境配置官方文档翻译与 将自定义c++方法绑定到lua的的方法
- 【黑马程序员】多态的概念和条件
- 关于linux下很多eclipse快捷键 失效的 问题
- Leetcode # 102 Binary Tree Level Order Traversal
- 机房收费系统个人重构版:透过文档谈文档驱动开发