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指定
(文件下载)
配置的三种方式:
传统配置, |
<!-- 添加--> <action name="user" class="cn.itcast.b_config.UserAction" method="add"> <result name="add" type="dispatcher">/index.jsp</result> </action> <!-- 删除 --> <action name="delete" class="cn.itcast.b_config.UserAction" method="delete"> <result name="delete" type="dispatcher">/index.jsp</result> </action> <!-- 修改 --> <action name="update" class="cn.itcast.b_config.UserAction" method="update"> <result name="update" type="dispatcher">/index.jsp</result> </action> |
通配符优化, |
<!-- 通配符配置 , 访问: http://localhost:8080/struts01/hello/a/b /user_update--> <!-- * 的值由运行时期访问时候,传入; {1} 表示第一个*的值; {2} 表示第二个 *的值 --> <action name="user_*" class="cn.itcast.b_config.UserAction" method="{1}"> <result name="{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的值栈中取值,而不用域对象,省去了将值放入域对象中的过程。
-
不继承任何类,也不实现任何接口。
-
publicclass TestAction1 {} |
-
实现Action 接口
-
publicclass TestAction2 implements Action{} |
-
继承ActionSupport类
-
publicclass TestAction3 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"/> |
<!-- http://localhost:8080/struts02/user!index ! 这个就表示动态方法调用,表示访问的actionName是user, 对应的action处理类的index方法执行请求处理 --> <action name="user" class="cn.itcast.b_constant.UserAction"> <result name="success">/success.jsp</result> <result name="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) 全局异常配置
<?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="config" extends="struts-default" namespace="/user">
<!-- 配置默认的action类,如果没有配置,使用struts-defalut配置文件中配置的默认类:ActionSupport <default-action-ref name=""></default-action-ref> -->
<!-- 全局视图,给当前包下所有的action用 --> <global-results> <result name="success">/success.jsp</result> <result name="error">/error_null.jsp</result> </global-results>
<!-- 全局异常配置 --> <!-- 当当前包下所有的action类中方法,只要出现空指针类型异常,就会跳转到error全局视图对应的错误页面 ! --> <global-exception-mappings> <exception-mapping result="error" exception="java.lang.NullPointerException"></exception-mapping> </global-exception-mappings>
<action name="user" class="cn.itcast.c_config.UserAction"> </action>
<action name="test" class="cn.itcast.c_config.TestAction"> </action>
<!-- 配置的各项目默认值,最简单的action配置 --> <!-- class : 默认处理的类是ActionSupport --> <!-- method: 默认执行execute方法 【execute方法success】 --> <!-- 必须配置全局视图,配置success视图对应的页面 --> <action name="myTest1"></action>
<!--应用: 可以通过myTest访问,跳转到WEB-INF下资源 --> <action name="myTest"> <result name="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。
publicclass DataAction 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> <input type="text" name="username"/> </td> </tr> <tr> <td>密码:</td> <td> <input type="password" name="pwd"/> </td> </tr> <tr> <td colspan="2"> <input type="submit" value=" 登陆 "> </td> </tr> </table> </form> </body> |
publicclass UserAction extends ActionSupport{
private String username; private String pwd;
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> <input type="password" name="user.pwd"/> </td> </tr> |
|
publicclass UserAction extends ActionSupport{
// 封装请求数据 (对象,一定要给get/set方法) private User user; 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接口
publicclass HelloInterceptor implements Interceptor{ // 服务器启动时候创建单例实例 public HelloInterceptor() { 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可以使用这个自定义的拦截器)
<?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="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}"> <result name="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 publicclass OgnlAction extends ActionSupport { // 根元素数据 private User user = 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 HTML PUBLIC "-//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:property value="user.id"/> <s:property value="user.name"/>
<!-- 1.2 非根元素取值 --> <s:property value="#request.request_data"/> <s:property value="#session.session_data"/> <s:property value="#application.application_data"/>
<!-- 2. 迭代标签 --> <br/>2.1 迭代list<br/><br/> <s:iterator var="user" value="#request.listUser" begin="2" end="9" status="st"> <s:property value="#st.even"/> <s:property value="#user.id"/> <s:property value="#user.name"/> <br/> </s:iterator>
<br/>2.2 迭代map (奇偶行变色) <br/><br/> <table> <tr> <td>序号</td> <td>编号</td> <td>名称</td> </tr> <s:iterator var="en" value="#request.mapUser" status="st"> <tr class=<s:property value="#st.even?'even':'odd'"/> > <td><s:property value="#st.count"/></td> <td><s:property value="#en.key"/></td> <td><s:property value="#en.value.name"/></td> </tr> </s:iterator> </table>
<br/>3. 文本框标签(UI, user interface 标签的其中一个)<br/> <s:form action="/ognl.action" method="post"> 用户名: <s:textfield name="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/>
<input type="submit" value="文件上传"/> </form> |
后台action对文件处理 |
publicclass UploadAction extends ActionSupport{ // 拿到请求数据: 普通数据、文件 private String userName; publicvoid setUserName(String userName) { this.userName = userName; }
// 获取jsp上传的文件对象 private File file1; // 获取上传的文件名 private String file1FileName; // 获取上传的文件类型(web.xml识别的文件类型) private String file1ContentType;
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配置文件中,给文件上传拦截器注入参数
<action name="upload_" class="cn.itcast.b_upload.UploadAction"> <!-- 如果指定执行拦截器,默认拦截器不会执行--> <interceptor-ref name="defaultStack"> <!-- 指定运行上传的文件的类型 <param name="fileUpload.allowedTypes">text/plain</param> -->
<!-- 允许上传的文件的后缀名(与上面配置取交集) --> <param name="fileUpload.allowedExtensions">txt,jar</param> </interceptor-ref>
<!-- 文件上传成功 --> <result name="success">/b/success.jsp</result>
<!-- 文件上传失败,struts会取找input 错误视图 --> <result name="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 */ publicclass DownAction 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"; }
// 二、文件下载调用的方法 public String down() throws Exception { return"down"; } // 1. 获取当前下载的文件名 private String fileName;//文件名跟前台一致,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>
<package name="upload" extends="struts-default"> <action name="upload_" class="cn.itcast.b_upload.UploadAction"> <!--如果指定执行拦截器,默认拦截器不会执行--> <interceptor-ref name="defaultStack"> <!-- 指定运行上传的文件的类型 <param name="fileUpload.allowedTypes">text/plain</param> -->
<!-- 允许上传的文件的后缀名(与上面配置取交集) --> <param name="fileUpload.allowedExtensions">txt,jar</param> </interceptor-ref>
<!-- 文件上传成功 --> <result name="success">/b/success.jsp</result> <!-- 文件上传失败,struts会取找input 错误视图 --> <result name="input">/b/error.jsp</result> </action>
<!-- 文件下载 --> <action name="down_*" class="cn.itcast.b_upload.DownAction" method="{1}"> <!-- 列表展示 --> <result name="list">/b/list.jsp</result> <!-- 文件下载标记,对应的是流 --> <result name="down" type="stream"> <!-- 指定下载的文件的类型:为二进制类型(tomcat服务器找) --> <param name="contentType">application/octet-stream</param> <!-- action类中返回文件流的方法 --> <param name="inputName">myFileStream</param> <!-- 下载显示的文件名 --> <param name="contentDisposition">attachment;filename=${downFileName}</param> <!-- 读取缓存区大小 --> <param name="bufferSize">1024</param> </result> </action> </package> </struts> |
15. Struts2 数据校验
数据校验分为前台校验和后台校验。
前台校验也称之为客户端校验,主要是通过JavaScript编程的方式进行表单数据的验证。特点:发生在客户端浏览器,效率高!同时不安全!
后台校验 也称之为服务器端校验,通过服务器代码进行验证。
特点:增大服务器运行压力,效率相对低!但安全、可靠!
对于两种校验方式,一般采用前台验证相对较多;有安全性要求的系统,一般前台验证 + 后台验证!
Struts2 采用的都是后台验证的方式。
a) Action中代码方式
前台的form表单:
<form action="${pageContext.request.contextPath}/login.action" method="post"> 用户名:<input type="text" name="user.userName"/> <br/> 密码:<input type="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 --> <result name="input">/login.jsp</result> |
页面用struts标签显示错误 |
<!-- 1. 显示struts运行时候产生的所有错误信息 <s:fielderror></s:fielderror> -->这种方式,会显示struts运行过程中,所有的错误信息。 或者,指定显示哪些错误: <form action="${pageContext.request.contextPath }/login.action" method="post"> 用户名:<input type="text" name="user.userName"/> <s:fielderror fieldName="userName"></s:fielderror> <br/> 密码:<input type="password" name="user.pwd"/> <s:fielderror fieldName="pwd"></s:fielderror> <br/> <input type="submit" value="登录" /> </form> |
Struts2 显示错误标签默认使用html中的列表标签(<ul><li>) ,这样会自动换行显示列表,这样极其影响用户体验。解决方案有两种:
可以在jsp页面中,加上标签样式。
<!-- 设置样式,修改struts的s:fielderror标签默认的样式 --> <style type="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配置文件: |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators> <!-- 一个field对应要验证的一个字段 --> <field name="user.userName"> <field-validator type="requiredstring"> <message>用户名不能为空!</message> </field-validator> </field> <field name="user.pwd"> <field-validator type="requiredstring"> <message>密码不能为空!</message> </field-validator> <field-validator type="stringlength"> <!—此处参数的name的值应在验证器 xwork-core-2.3.4.1.jar/com.opensymphony.xwork2.validator.validators/default.xml对应的类中查找(定义的变量) --> <param name="minLength">3</param> <param name="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相关标签。
框架中国际化的实现首先需要通过常量配置加载资源文件(只需要指定资源文件的基础名即可)
<!-- 通过常量加载资源文件 --> <constant name="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
publicclass DateConverter extends StrutsTypeConverter{ /* * 转换器支持:多种日志格式的转换, yyyy-MM-dd * yyyyMMDD yyyy年MM月dd日 yyyy/MM/dd ...... */ // 预定义支持的转换格式 private DateFormat dateFormat[] = { 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/> <input type="submit" value="注册" /> </form> |
后台action的代码 |
//1. 实现模型驱动接口 publicclass UserAction extends ActionSupport implements ModelDriven<User>{ //2. 要封装的对象一定要实例化 private User user = 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(); } |