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)       服务器启动时

      1. 加载web.xml;

      2. 创建核心过滤器实例

      3. 执行过滤器的init()方法,执行初始化,加载下面3个配置文件:

        1. 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" />

        1. struts-plugin.xml,

          struts2的插件相关配置!

        2. struts.xml

用户编写的struts配置!

配置:  请求的路径与处理的action类之间的映射!

b)       访问时

      1. struts把用户请求,封装为ActionMapper对象

      2. 创建配置管理类对:ConfigurationManager。读取struts.xml,创建ActionProxy, 即Action的代理对象。

      3. 通过代理对象创建访问的action实例

      4. 通过ActionInvocation调用默认拦截器中的18个拦截器(执行顺序从上往下)

      1. 执行action中的处理请求的方法

      2. 执行完Action中业务处理方法后,又回到拦截器;

      3. 最后,由服务器响应用户。

执行流程时序图:

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 开发的三种方式:

    1. 不继承任何类,也不实现任何接口。

publicclass TestAction1 {}

 

    1. 实现Action 接口

publicclass TestAction2implements Action{}

 

    1. 继承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

    ! 这个就表示动态方法调用,表示访问的actionNameuser,对应的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

                   actionNameuser没有配置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)       数据处理(后台到前台传送数据)

      1. 方式一:通过ServletActionContext, 获取原生态的servlet api!

注意:因为要引入原生的servletapi!所以要导入Servlet相关包!Strutsservlet耦合!

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");

    }

 

      1. 方式二: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");

    }

 

      1. 方式三:通过接口注入的方式

访问strutsaction时,会经过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中需要定义对应变量的setget方法。

      1. 封装到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";

    }

}

 

      1. 封装到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的拦截器

²  概念

    1. 框架之所以能提高开发效率是因为已经实现了一些常用的功能,是软件的半成品。Struts同样也是,struts预先实现的功能都是通过一个个的拦截器实现的。一个拦截器代表一个功能,用户可以根据自己的需要,自有组装。

    2. 拦截器栈

为了方便对拦截器的引用,strtus允许定义拦截器栈, 一个拦截器栈中可以包含多个拦截器,栈中拦截器的顺序就是执行的顺序。拦截器栈中也可以引用另外的栈。如果用户没有指定使用哪些拦截器,也就是说没有制定使用哪个拦截器栈, struts默认在创建action之后,自动调用默认栈, 即”defaultStack”, 这个栈里面定义了默认执行的18个拦截器

    1. 自定义拦截器

当struts提供的拦截器满足不了我们的需求时,用户可以自定义拦截器,如果自定了拦截器,执行的时候,默认的栈一定要执行!(否则不能使用struts的功能,因为struts的功能是通过拦截器实现的)

    1. 拦截器就是用来拦截Action请求的!

  • 拦截器的使用

  1. 写一个拦截器类,实现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();//配置的actionname属性值

        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时的执行顺序

  1. 在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表达式语言(了解)

    1. Ognl表达式取值和EL表达式取值的区别

      EL表达式时jsp页面取值的标准;

      Ognl表达式时struts2标签取值的标准,必须配合struts标签使用

    2. Struts标签能在jsp页面取到值的原因:Struts运行数据都存储到值栈中,值栈对象又存到request域对象中;转发到的页面就可以拿到request中值栈对象,从而可以使用struts标签从值栈中取值!

    3. Ognl表达式简介

OGNLObject 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的接口。

    1. 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运行时期产生的数据!

  1. ValueStack 存储结构

  1. Struts中对域对象的重新封装

  parameters: 该 Map 中包含当前请求的请求参数

  request: 该 Map 中包含当前 request 对象中的所有属性

  session: 该 Map 中包含当前 session 对象中的所有属性

  application:该 Map 中包含当前 application  对象中的所有属性

  attr: 该 Map 按如下顺序来检索某个属性: request, session,application

  1. 获取值栈对象的两种方式

方式一:从request对象获取

HttpServletRequest request = ServletActionContext.getRequest();

        ValueStack vs1 = (ValueStack) request.getAttribute("struts.valueStack");

 

方式二:通过ActionContext获取值栈对象

ValueStack vs = ActionContext.getContext().getValueStack();

  1. 操作值栈中的元素

    操作根元素集合(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();

}

  1. 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组件)

      1. 只要表单符合规则,在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冲突。  换个名称!

      1. 文件上传细节:

超出文件大小时会报错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中查看

      1. 限制文件上传类型

可以在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代码中判断,因为判断时文件已经上传成功,只不过是临时文件而已。

      1. 文件上传失败的错误处理

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页面中,加上标签样式。

<!-- 设置样式,修改strutss: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)       局部类型转换器

      1. 写类型转换器类

自定义类型转换器(以日期转换器为例),继承类StrutsTypeConverter。重写方法public Object convertFromString

publicclassDateConverter extends StrutsTypeConverter{

    /*

     * 转换器支持:多种日志格式的转换,yyyy-MM-dd

     * yyyyMMDD   yyyyMMdd   yyyy/MM/dd  ......

     */

    // 预定义支持的转换格式

    private DateFormatdateFormat[] = {

        new SimpleDateFormat("yyyy-MM-dd"),

        new SimpleDateFormat("yyyyMMDD"),  

        new SimpleDateFormat("yyyy/MM/dd"),

        new SimpleDateFormat("yyyyMMdd")

    };

 

    /**

     * 把表单提交的字符串数据,转换为指定的目标类型

     * @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;

    }

 

}

 

      1. 配置类型转换器(通知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)的形式,后台就可以封装到对象中。实现步骤如下:

    1. 实现模型驱动的接口;

    2. 对要进行封装的对象进行实例化;

    3. 实现接口中的方法(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();

    }

 

 

0 0
原创粉丝点击