Struts2学习笔记

1.介绍struts2框架

    问题:什么是框架,框架有什么用?
        框架 是 实现部分功能的代码 (半成品),使用框架简化企业级软件开发 ,提高开发效率。
        学习框架 ,清楚的知道框架能做什么? 还有哪些工作需要自己编码实现 ? 

    问题:什么是struts2框架,它有什么用?
        Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。
        其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心
        struts2=struts1+webwork;            
        struts2框架是apache产品。         
        struts2是一个标准的mvc框架。  javaweb中的model2模式就是一个mvc模式。  model2=servlet+jsp+javaBean           
        struts2框架是在javaweb开发中使用的。           
        使用struts2框架,可以简化我们的web开发,并且降低程序的耦合度。

    类似于struts2框架的产品 :
        struts1  webwork  jsf  springmvc

        ssh---struts2 spring hibernate
        ssi---springmvc spring ibatis

    XWork---它是webwork核心
    Xwork提供了很多核心功能:前端拦截机(interceptor),运行时表单属性验证,类型转换,
    强大的表达式语言(OGNL – the Object Graph Navigation Language),
    IoC(Inversion of Control反转控制)容器等    
-----------------------------------------------------------------------------------
struts2快速入门:
    index.jsp------>HelloServlet-------->hello.jsp  web开发流程.
    index.jsp------>HelloAction--------->hello.jsp  struts2流程

    1.导入jar包
        下载struts2的jar包  struts-2.3.15.1-all 版本.

        struts2的目录结构:
            apps: 例子程序
            docs:文档
            lib:struts2框架所应用的jar以及插件包               
            src:源代码  
                core  它是struts2的源代码
                xwork-core struts2底层使用了xwork,xwork的源代码

        注意:在struts2开发,一般情况下最少导入的jar包,去apps下的struts2-blank示例程序中copy

    2.创建index.jsp页面
      创建hello.jsp页面

    3.对struts2框架进行配置
        1.web.xml文件中配置前端控制器(核心控制器)-----就是一个Filter
            目的:是为了让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>

        2.创建一个struts.xml配置文件 ,这个是struts2框架配置文件。
            目的:是为了struts2框架流程可以执行。

            名称:struts.xml
            位置:src下(classes下)

    4.创建一个HelloAction类
        要求,在HelloAction类中创建一个返回值是String类型的方法,注意,无参数。
        public String say(){
            return "good";
        }

    5.在struts.xml文件中配置HelloAction

        <package name="default" namespace="/" extends="struts-default">
            <action name="hello" class="cn.itcast.action.HelloAction"
                method="say">
                <result name="good">/hello.jsp</result>
            </action>
        </package>

    6.在index.jsp中添加连接,测试
        <a href="${pageContext.request.contextPath}/hello">第一次使用struts2</a>
        在地址栏中输入:http://localhost/struts2_day01/index.jsp  访问连接,就可以看到
        HelloAction类中的say方法执行了,也跳转到了hello.jsp.

--------------------------------------------------------------------------
对入门程序进行流程分析 


--------------------------------------------------------------------------
模仿struts2流程完成入门程序:
    index.jsp
    hello.jsp
    HelloAction
    struts.xml
-----------------
1.创建一个Filter----StrutsFilter
2.在web.xml文件中配置StrutsFilter
    <filter>
        <filter-name>struts</filter-name>
        <filter-class>cn.itcast.filter.StrutsFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
3.在StrutsFilter中完成拦截操作,并访问Action中的方法,跳转到hello.jsp页面操作.

        // 2.1 得到请求资源路径
        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();
        String path = uri.substring(contextPath.length() + 1);

        // System.out.println(path); // hello

        // 2.2 使用path去struts.xml文件中查找某一个<action name=path>这个标签
        SAXReader reader = new SAXReader();
        // 得到struts.xml文件的document对象。
        Document document = reader.read(new File(this.getClass()
                .getResource("/struts.xml").getPath()));

        Element actionElement = (Element) document
                .selectSingleNode("//action[@name='" + path + "']"); // 查找<action
                                                                        // name='hello'>这样的标签

        if (actionElement != null) {
            // 得到<action>标签上的class属性以及method属性
            String className = actionElement.attributeValue("class"); // 得到了action类的名称
            String methodName = actionElement.attributeValue("method");// 得到action类中的方法名称。

            // 2.3通过反射,得到Class对象,得到Method对象
            Class actionClass = Class.forName(className);
            Method method = actionClass.getDeclaredMethod(methodName);

            // 2.4 让method执行.
            String returnValue = (String) method.invoke(actionClass
                    .newInstance()); // 是让action类中的方法执行,并获取方法的返回值。

            // 2.5
            // 使用returnValue去action下查找其子元素result的name属性值,与returnValue做对比。
            Element resultElement = actionElement.element("result");
            String nameValue = resultElement.attributeValue("name");

            if (returnValue.equals(nameValue)) {
                // 2.6得到了要跳转的路径。
                String skipPath = resultElement.getText();

                // System.out.println(skipPath);

                request.getRequestDispatcher(skipPath).forward(request,
                        response);
                return;
            }

}

struts2的流程分析以及工具配置

1.流程分析
    请求 ---- StrutsPrepareAndExecuteFilter 核心控制器 ----- Interceptors 拦截器(实现代码功能 ) ----- Action 的execute --- 结果页面 Result 
    * 拦截器 在 struts-default.xml定义
    * 执行拦截器 是 defaultStack 中引用拦截器 

    ---- 通过源代码级别断点调试,证明拦截器是执行 


2.关于手动配置struts.xml文件中提示操作

     如果安装Aptana编辑器 ,请不要用Aptana自带xml编辑器 编写struts2配置文件 
     struts.xml提示来自于 DTD约束, 
        <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
        如果可以上网,自动缓存dtd,提供提示功能
        如果不能上网,也可以配置本地DTD提示 

    *** 导入DTD时,应该和配置DTD版本一致 

3.关联struts2源文件
    如果是com.opensymphony.xxx   在xwork-core下
    如果是org.apache.struts2     在core下


4.使用插件  struts2-config-browser-plugin-2.3.15.1
    提供在浏览器中查看 struts2 配置加载情况 

    将解压struts2/lib/struts2-config-browser-plugin-2.3.7.jar 复制WEB-INF/lib下 

    访问 http://localhost/struts2_day01/config-browser/index.action 查看 struts2配置加载情况

=================================================================================================
struts2配置(重点)
1.struts2配置文件加载顺序
struts2框架要能执行,必须先加载StrutsPrepareAndExecuteFilter.

    在StrutsPrepareAndExecuteFilter的init方法中对Dispatcher进行了初始化.
    在Dispatcher类中定义的init方法内就描述了struts2配置文件加载的顺序

        init_DefaultProperties(); // [1]   ----------  org/apache/struts2/default.properties 
        init_TraditionalXmlConfigurations(); // [2]  --- struts-default.xml,struts-plugin.xml,struts.xml
        init_LegacyStrutsProperties(); // [3] --- 自定义struts.properties 
        init_CustomConfigurationProviders(); // [5]  ----- 自定义配置提供
        init_FilterInitParameters() ; // [6] ----- web.xml 
        init_AliasStandardObjects() ; // [7] ---- Bean加载 

        1.default.properties文件
            作用:定义了struts2框架中所有常量
            位置: org/apache/struts2/default.properties 

        2.struts-default.xml
            作用:配置了bean,interceptor,result等。
            位置:在struts的core核心jar包.

          struts-plugin.xml
            它是struts2框架中所使用的插件的配置文件。
          struts.xml              
            我们使struts2所使用的配置文件。

        3.自定义的struts.properties
            就是可以自定义常量。

        4.web.xml

        在开发中,后加载文件中的配置会将先加载文件中的配置覆盖。

        default.properties
        struts-default.xml
        struts.xml

-----------------------------------------------------------------------         
2.关于Action的配置

    1.<package>  作用:是用于声明一个包。用于管理action。
        1.name     它用于声明一个包名,包名不能重复,也就是它是唯一的。 
        2.namespace  它与action标签的name属性合并确定了一个唯一访问action的路径。
        3.extends  它代表继承的包名。
        4.abstrace 它可以取值为true/false,如果为true,代表这个包是用于被继承的。
    2<action>  用于声明 一个action
        1.name  就是action的一个名称,它是唯一的(在同包内) 它与package中的namespace确定了访问action的路径。
        2.class Action类的全名
        3.method 要访问的Action类中的方法的名称,方法无参数 ,返回值为String.
    3.<result> 用于确定返回结果类型
        1.name  它与action中的方法返回值做对比,确定跳转路径。

    关于action配置其它细节:
        1.关于默认值问题
            <package namespace="默认值"> namespace的默认值是""
            <action class="默认值"  method="默认值">
                class的默认值是  com.opensymphony.xwork2.ActionSupport

                method的默认值是  execute
            <result\d   X name="默认值"> name的默认值是 "success"       

        2.关于访问action的路径问题   
            现在的action的配置是:
            <package name="default" namespace="/" extends="struts-default">
                <action name="hello" class="cn.itcast.action.DefaultAction">
                    <result>/hello.jsp</result>
                </action>
            </package>

            当我们输入:
            http://localhost/struts2_day01_2/a/b/c/hello
            也访问到了action。

            原因:struts2中的action被访问时,它会首先查找
             1.namespace="/a/b/c"  action的name=hello  没有.
             2.namespace="/a/b     action的name=hello  没有
             3.namespace="/a"      action的name=hello  没有
             4.namespace="/"        action的name=hello  查找到了.

             如果最后也查找不到,会报404错误.

        3.默认的action。
            作用:处理其它action处理不了的路径。

            <default-action-ref name="action的名称" />
            配置了这个,当访问的路径,其它的action处理不了时,就会执行name指定的名称的action。

        4.action的默认处理类
            在action配置时,如果class不写。默认情况下是 com.opensymphony.xwork2.ActionSupport。

            <default-class-ref class="cn.itcast.action.DefaultAction"/>
            如果设置了,那么在当前包下,默认处理action请的的处理类就为class指定的类。

关于常量配置
    default.properties 它声明了struts中的常量。

    问题:人为设置常量,可以在哪些位置设置 ?
        1.struts.xml(应用最多)
            <constant name="常量名称" value="常量值"></constant>
        2.struts.properties(基本不使用)          
        3.web.xml(了解)
            配置常量,是使用StrutsPrepareAndExecuteFilter的初始化参数来配置的.
            <init-param>
                <param-name>struts.action.extension</param-name>
                <param-value>do,,</param-value>
            </init-param>

    常用常量
        struts.action.extension=action,, 
        这个常量用于指定strus2框架默认拦截的后缀名.

        <constant name="struts.i18n.encoding" value="UTF-8"/>  
            相当于request.setCharacterEncoding("UTF-8"); 解决post请求乱码 

        <constant name="struts.serve.static.browserCache" value="false"/> 
            false不缓存,true浏览器会缓存静态内容,产品环境设置true、开发环境设置false  

        <constant name="struts.devMode" value="true" />  

提供详细报错页面,修改struts.xml后不需要重启服务器 (要求)

struts.xml文件的分离:

    目的:就是为了阅读方便。可以让一个模块一个配置文件,在struts.xml文件中通过
    <include file="test.xml"/>导入其它的配置文件。

======================================================================================================
Action

1.关于Action类的创建方式介绍:
    有三种方式
    1.创建一个POJO类.
        简单的Java对象(Plain Old Java Objects)
        指的是没有实现任何接口,没有继承任何父类(除了Object)

        优点:无耦合。
        缺点:所以工作都要自己实现。

        在struts2框架底层是通过反射来操作:
            * struts2框架 读取struts.xml 获得 完整Action类名 
            * obj = Class.forName("完整类名").newInstance();
            * Method m = Class.forName("完整类名").getMethod("execute");  m.invoke(obj); 通过反射 执行 execute方法

    2.创建一个类,实现Action接口.  com.opensymphony.xwork2.Action

        优点:耦合低。提供了五种结果视图,定义了一个行为方法。
        缺点:所以工作都要自己实现。

        public static final String SUCCESS = "success";  // 数据处理成功 (成功页面)
        public static final String NONE = "none";  // 页面不跳转  return null; 效果一样
        public static final String ERROR = "error";  // 数据处理发送错误 (错误页面)
        public static final String INPUT = "input"; // 用户输入数据有误,通常用于表单数据校验 (输入页面)
        public static final String LOGIN = "login"; // 主要权限认证 (登陆页面)

    3.创建一个类,继承自ActionSupport类.  com.opensymphony.xwork2.ActionSupport
        ActionSupport类实现了Action接口。

        优点:表单校验、错误信息设置、读取国际化信息 三个功能都支持.
        缺点:耦合度高。

    在开发中,第三种会使用的比较多.
--------------------------------------------------------------------------  
关于action的访问:

    1.通过设置method的值,来确定访问action类中的哪一个方法.
        <action name="book_add" class="cn.itcast.action.BookAction" method="add"></action>
        当访问的是book_add,这时就会调用BookAction类中的add方法。         
        <action name="book_update" class="cn.itcast.action.BookAction"  method="update"></action>
        当访问的是book_update,这时就会调用BookAction类中的update方法。

    2.使用通配符来简化配置
        1.在struts.xml文件中
            <action name="*_*" class="cn.itcast.action.{1}Action" method="{2}"></action>
        2.在jsp页面上
            book.jsp
                <a href="${pageContext.request.contextPath}/Book_add">book add</a><br>
                <a href="${pageContext.request.contextPath}/Book_update">book update</a><br>
                <a href="${pageContext.request.contextPath}/Book_delete">book delete</a><br>
                <a href="${pageContext.request.contextPath}/Book_search">book search</a><br>
            product.jsp
                <a href="${pageContext.request.contextPath}/Product_add">product add</a><br>
                <a href="${pageContext.request.contextPath}/Product_update">product update</a><br>
                <a href="${pageContext.request.contextPath}/Product_delete">product delete</a><br>
                <a href="${pageContext.request.contextPath}/Product_search">product search</a><br>

            当访问book add时,这时的路径是  Book_add,那么对于struts.xml文件中.
            第一个星就是   Book
            第二个星就是   add
            对于{1}Action---->BookAction
            对于method={2}--->method=add

        使用通配符来配置注意事项:
            1.必须定义一个统一的命名规范。
            2.不建议使用过多的通配符,阅读不方便。
    ----------------------------------------------
    3.动态方法调用    (了解)
        在struts.xml文件中
             <action name="book" class="cn.itcast.action.BookAction"></action>
        访问时路径: http://localhost/struts2_day01_2/book!add
            就访问到了BookAction类中的add方法。

        对于book!add 这就是动态方法调用。

        注意:struts2框架支持动态方法调用,是因为在default.properties配置文件中设置了
             动态方法调用为true.

struts.enable.DynamicMethodInvocation = true

在struts2框架中获取servlet api

对于struts2框架,不建议直接使用servlet api;

在struts2中获取servlet api有三种方式:
    1.通过ActionContext来获取
        1.获取一个ActionContext对象。
            ActionContext context=ActionContext.getContext();
        2.获取servlet api
            注意:通过ActionContext获取的不是真正的Servlet api,而是一个Map集合。

            1.context.getApplication()
            2.context.getSession()
            3.context.getParameter();---得到的就相当于request.getParameterMap()
            4.context.put(String,Object) 相当于request.setAttribute(String,String);


    2.注入方式获取(这种方式是真正的获取到了servlet api)

        1.要求action类必须实现提定接口。
            ServletContextAware : 注入ServletContext对象
            ServletRequestAware :注入 request对象
            ServletResponseAware : 注入response对象

        2.重定接口中的方法。             
            private HttpServletRequest request;
        3.声明一个web对象,使用接口中的方法的参数对声明的web对象赋值. 
            public void setServletRequest(HttpServletRequest request) {
                this.request = request;
            }

        扩展:分析其实现:
            是使用struts2中的一个interceptor完成的.
            <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>

             if (action instanceof ServletRequestAware) { //判断action是否实现了ServletRequestAware接口
                HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST); //得到request对象.
                ((ServletRequestAware) action).setServletRequest(request);//将request对象通过action中重写的方法注入。
            }

    3.通过ServletActionContext获取.
        在ServletActionContext中方法都是static。           
        getRequest();
        getResposne();

getPageContext();

Result结果类型

    <result>标签
        1.name  与action中的method的返回值匹配,进行跳转.

        2.type  作用:是用于定义跳转方式

        对于type属性它的值有以下几种:
            在struts-default.xml文件中定义了type可以取的值

             <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
            <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
            <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
            <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
            <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
            <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
            <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
            <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
            <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
            <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />

        必会: chain  dispatcher  redirect redirectAction  stream

            dispatcher:它代表的是请求转发,也是默认值。它一般用于从action跳转到页面。
            chain:它也相当于请求转发。它一般情况下用于从一个action跳转到另一个action。

            redirect:它代表的是重定向  它一般用于从action跳转到页面
            redirectAction: 它代表的是重定向  它一般用于从action跳转另一个action。

            stream:代表的是服务器端返回的是一个流,一般用于下载。

        了解: freemarker  velocity

    ----------------------------------------------------
    局部结果页面与全局结果页面
         局部结果页面 和 全局结果页面 
        <action name="result" class="cn.itcast.struts2.demo6.ResultAction">
                    <!-- 局部结果  当前Action使用 -->
                    <result name="success">/demo6/result.jsp</result> 
        </action>

        <global-results>
                    <!-- 全局结果 当前包中 所有Action都可以用-->
                    <result name="success">/demo6/result.jsp</result>
        </global-results>

1.struts2中获取请求参数

在struts2中action是什么?(struts2是一个mvc框架)    
    V:jsp
    M:action
    C:action  StrutsPrepareAndExecuteFilter

在struts2中获取请求参数:
    1.属性驱动
        1.直接将action做一个model,就可以得到请求参数.
            问题1:action封装请求参数,会不会存在线程安全问题?
                不会:因为每一次请求,都是一个新的action。
            缺点:需要单独定义javaBean,将action中属性copy到javaBean中。 
            优点:简单。              
            这种方式 ,底层是通过反射来实现的。

        2.在action中声明一个model。
            private User user;提供get/set方法

            在页面上使用ognl来描述
            <input type="text" name="user.username">

            优点:简单,解决了第一种封装的问题
            缺点:在页面上使用了ognl表达式,页面不通用了。

            问题:这种方式,数据是怎样封装的?
                是通过struts2中的interceptor进行了数据封装.
                 <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>



    2.模型驱动(在开发中应用比较多)
        步骤:
            1.让action类实现ModelDriven
            2.重写getModel方法
            3.在action中实现化一个model对象,让getModel方法返回这个对象。

            public class Login3Action extends ActionSupport implements ModelDriven<User> {

                private User user = new User();

                public User getModel() {
                    return user;
                }
            }
        优点:解决了属性驱动存在的问题
        缺点:一次只能封装一个model对象.

         struts2 有很多围绕模型驱动的特性 
        * <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> 为模型驱动提供了更多特性

--------------------------------------------------------------------------
扩展:
    1.将数据封装到List集合
        页面:
            username1:<input type="text" name="users[0].username"><br>
            password1:<input type="password" name="users[0].password"><br>

            username2:<input type="text" name="users[1].username"><br>
            password2:<input type="password" name="users[1].password"><br>

        action类:
            private List<User> users;
            get/set方法

    2.将数据封装到Map集合
        页面:
            username1:<input type="text" name="map['aaa'].username"><br>
            password1:<input type="password" name="map['aaa'].password"><br>

            username2:<input type="text" name="map['bbb'].username"><br>
            password2:<input type="password" name="map['bbb'].password"><br>

        action类:
            private Map<String, User> map;
            提供get/set

===================================================================================================
struts2中提供的类型转换

在web中我们使用beanutils直接将表单数据封装到javaBean中。---类型转换

struts2中action得到请求参数,也可以直接封装到javaBean.

    struts2 内部提供大量类型转换器,用来完成数据类型转换问题 
    boolean 和 Boolean
    char和 Character
    int 和 Integer
    long 和 Long
    float 和 Float
    double 和 Double
    Date 可以接收 yyyy-MM-dd格式字符串
    数组  可以将多个同名参数,转换到数组中
    集合  支持将数据保存到 List 或者 Map 集合

    例如:日期类型,我们传递  yyyy-MM-dd  yyyy年MM月dd日格式都可以,但是如果是yyyy/MM/dd
        就会出现问题.

关于struts2中的类型转换器:
    struts2中的类型转换器根接口是:com.opensymphony.xwork2.conversion.TypeConverter。

-------------------------------------------------------
自定义类型转换器:

    步骤:
        1.创建一个类实现TypeConverter接口.
        2.重写接口中方法,实现类型转换操作.
        3.注册类型转换器.

    详解说明:
        1.创建一个自定义类型转换器
            1.实现TypeConverter需要重写
                public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
                如果实现接口,这个方法参数太多(6个)

            2.不推荐实现接口,可以继承 DefaultTypeConverter类
                优点:重写的方法参数没有那么多
                 public Object convertValue(Map<String, Object> context, Object value, Class toType) {
                        return convertValue(value, toType);
                }

            3.个人推荐使用 继承DefaultTypeConverter类的一个子类StrutsTypeConverter.
                原因:在这个类中将从页面传递的数据怎样封装,以及action中的数据怎样在页面上显示做了分离.

                  public abstract Object convertFromString(Map context, String[] values, Class toClass);
                  public abstract String convertToString(Map context, Object o);



        2.怎样注册一个自定义类型转换器.

            1.局部--针对于action
                 配置文件所在位置以及名称:  在Action类所在包 创建 Action类名-conversion.properties , 
                 配置文件书写:    格式 : 属性名称=类型转换器的全类名 
            2.局部--针对于model
                 配置文件所在位置以及名称:  在model类所在包 创建 model类名-conversion.properties , 
                 配置文件书写:    格式 : 属性名称=类型转换器的全类名 
            3.全局
                配置文件所在位置以及名称:在src下创建一个xwork-conversion.properties
                配置文件书写:  格式:  要转换的类型全名=类型转换器的全类名 

    -----------------------------------------------------------------------------
    注意:
        对于struts2中类型转换器,如果表单数据提交时,将数据向model封装,出现了问题,会报错:
        No result defined for action cn.itcast.action.RegistAction and result input

        上面的意思是说,在RegistAction的配置中没有配置input结果视图.
        <action name="regist" class="cn.itcast.action.RegistAction">
            <result name="input">/success.jsp</result>
        </action>
        如果配置了,出现类型转换问题,就会跳转到input指定的视图。

        问题:为什么会向input视图跳转?
            是因为struts2中的拦截器(interceptor).

            在struts2中的
                 <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
                 用于记录类型转换问题

            在struts2中
                 <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
                 用于得到问题,向input视图跳转。

        关于错误信息展示:

            通过分析拦截器作用,得知当类型转换出错时,自动跳转input视图 ,在input视图页面中 <s:fieldError/> 显示错误信息    
            * 在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息

如果是自定义类型转换器,出现类型转换问题,要跳转到input视图,在类型转换器中,必须抛出异常才可以。

关于struts2提供的数据校验

在开发中,请求参数是需要校验的。
    客户端校验---->js
    服务器校验---->java代码。

struts2中提供的校验-----服务器端校验。

分成两种:
    1.手动校验(编码校验)
    2.配置校验(annotation,xml) 我们讲的是xml。

1.手动校验:(了解)
    要求:action类必须继承自ActionSupport。需要重写一个方法 validate

    通过测试发现在action中重写的validate方法执行了。并且是在请求处理方法(execute)之前执行的。


    对于struts2提供的校验,它也是通过拦截器实现的。

    问题:在validate方法中怎样存储校验错误信息?

        在validate方法中   this.addFieldError(Sting name,String value);

    问题:在页面上怎样获取错误信息?(在input视图上)
        <s:fielderror> 展示所有错误信息

        <s:fielderror fieldName="">展示特定名称的错误信息.

    ------------------  
    问题:在同一个Action中有多个请求处理方法(login,regist)那么有些方法是需要校验的,有些是不需要的,怎样处理?
        解决方案:创建一个名称叫   validate+请求处理方法名   例如:请求处理方法叫  regist()  校验 的方法名 validateRegist().

-------------------------------------------------------------------------------------------------------------
2.配置校验(xml)
    struts2的校验框架。
    已经完成了校验操作(做了很多校验方法)。
    而我们在使用时,只需要将它们调用就可以(通过配置文件)

    要求:action类必须继承自ActionSupport。

    问题:配置文件怎样配置?

        位置:xml文件要与action类在同一个包下
        名称:action类名-validation.xml
        约束: xwork-core-2.3.7.jar 中 xwork-validator-1.0.3.dtd 下
            <!DOCTYPE validators PUBLIC
            "-//Apache Struts//XWork Validator 1.0.3//EN"
            "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
        书写:
            1.根元素
                <validators>
            2.子元素
                <field name="属性名称"></field>     

            3.<field>子元素    
                <field-validator type="校验器"> 这个是指定校验器
                问题:校验器有哪些?
                    xwork-core-2.3.7.jar 中 /com/opensymphony/xwork2/validator/validators/default.xml下

                    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
                    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
                    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
                    <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
                    <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
                    <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
                    <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
                    <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
                    <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
                    <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
                    <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
                    <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
                    <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
                    <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
                    <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
                    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>

            4.<field-validator>子元素  
                <message>错误信息</message>

            5.<field-validator>子元素
                <param name="">值</param>
                用于指定校验器中的参数.
    ------------------------------------------------------------------------------------------------
    介绍:关于配置校验中的校验器: 

        * required (必填校验器,要求被校验的属性值不能为null)
        * requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
        * stringlength (字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
        * regex (正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
        * int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
        * double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
        * fieldexpression (字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
        * email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)
        * url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)
        * date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)

    ------------------------------------------------------------------------------------------- 
    问题:通过配置校验,怎样处理在同一个action中存在多个请求处理方法校验问题?

        只需要将校验xml文件名称修改就可以。
        action类名-valication.xml 现在要对action类中某一个方法校验。

        action类名-action名称-validation.xml.

从struts2.1开始,struts2 引入了Convention插件来支持零配置
使用约定无需struts.xml或者Annotation配置
需要 struts2-convention-plugin-2.3.7.jar 、asm-*.jar(三个)
插件会自动搜索action、actions、struts、struts2包下所有Java类
所有实现了com.opensymphony.xwork2.Action的Java类
所有类名以Action结尾的Java类
下面类名都符合Convention插件
cn.itcast.struts2.HelloAction
cn.itcast.actions.books.BookSearchAction
cn.itcast.struts.user.UserAction
cn.itcast.estore.action.test.LoginAction

struts2-convention-plugin-2.3.7.jar 中struts-plugin.xml重要常量
默认扫描包
不扫描
默认扫描以Action结尾的类
结果result页面存放位置
Action类文件重新自动加载

如果Action类名包含Action后缀,将Action后缀去掉
将Action类名的驼峰写法,转成中划线写法
例如:
cn.itcast.struts2.HelloAction 映射到 /hello.action
cn.itcast.actions.books.BookSearchAction 映射到 /books/book-search.action
cn.itcast.struts.user.UserAction 映射到 /user/user.action
cn.itcast.estore.action.test.LoginAction 映射到 /test/login.action

默认情况下,Convention总会到Web应用的WEB-INF/content路径下定位结果资源

约定: actionName + resultCode + suffix
例如:
访问cn.itcast.struts.user.UserAction返回success
Convention优先使用 WEB-INF/content/user/user-success.jsp
如果user-success.jsp不存在,会使用user-success.html
如果user-success.html不存在,会使用user.jsp

1.国际化

1、 国际化原理 ? 什么是国际化 ? 
同一款软件 可以为不同用户,提供不同语言界面  ---- 国际化软件
需要一个语言资源包(很多properties文件,每个properties文件 针对一个国家或者语言 ,
通过java程序根据来访者国家语言,自动读取不同properties文件 )

2、 资源包编写 
    properties文件命名 :  基本名称_语言(小写)_国家(大写).properties
例如 :
    messages_zh_CN.properties 中国中文
    messages_en_US.properties 美国英文
3、 ResourceBundle 根据不同Locale(地域信息),读取不同国家 properties文件
ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.US);

----------------------------
struts2中国际化:
    struts2中对国际化进行了封装,我们只需要根据其提供的API进行访问就可以。

    问题1:在struts2中国际化时properties文件怎样定义?(怎样定义properties)

        1.全局
            需要通过一个常量来声明.
            struts.custom.i18n.resources=testmessages,testmessages2

            对于properties配置文件可以放置在任意位置

            <constant name="struts.custom.i18n.resources" value="message"> 代表message.properties在src下
            <constant name="struts.custom.i18n.resources" value="cn.itcast.i18n.resource.message"> 代表message.properties在cn.itcast.i18n.resource包下.
        2.局部
            1.针对于action类
                位置:与action类在同一个包下.
                名称:ActionClassName.properties.
                这个配置文件只对当前action有效。
            2.针对于package下所有action
                位置:在指定的包下
                名称:package.properties
            3.jsp页面临时使用某一个properties文件.
                <s:i18n name="cn.itcast.action.package"></s:i18n>


    问题2:在struts2中国际化操作可以在哪些位置使用?(在哪此位置上使用)

        1.action类中使用

        2.配置文件中使用<validation.xml>

        3.在jsp页面上使用


    问题3:怎样在struts2中操作国际化?(怎样使用)
        1.在action类中使用
            前提:action类要继承ActionSupport类。

            getText(String name)就可以获取配置文件中对应名称的值。

        2.在validation.xml文件中使用

            <message key="名称"/>

        3.在jsp页面上使用

            <s:text name="名称"> 如果没有使用<s:i18n name="">来指定,会从全局配置文件中获取。
            如果要从某一个配置文件中获取,通过name属性来指定,  包名.配置文件名称 .
--------------------------------------------------------
在struts2中国际化配置文件中使用动态文本
    1.action中怎样使用
        msg=hello world  {0}
        this.getText("msg",new String[]{"tom"})

        结果就是 hello world tom

    2.jsp页面上怎样使用
        msg=hello world  {0}

        <s:i18n name="cn.itcast.action.I18nDemo1Action">
            <s:text name="msg">
                <s:param>张三</s:param>
            </s:text>
        </s:i18n>

结果就是 hello world 张三.

2.拦截器(interceptor)
介绍拦截器:
struts2拦截器使用的是AOP思想。
AOP的底层实现就是动态代理。
拦截器 采用 责任链 模式
* 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。
* 责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行

    struts2中在struts-default.xml文件中声明了所有的拦截器。
    而struts2框架默认使用的是defaultStack这个拦截器栈。
    在这个拦截器栈中使用了18个拦截器。简单说,struts2框架
    在默认情况下,加载了18个拦截器。       
-----------------------------------------------------
1.struts2中怎样使用拦截器

    问题:使用拦截器可以做什么?
        可以通过使用拦截器进行控制action的访问。例如,权限操作。

    怎样使用拦截器?
        1.创建一个Interceptor  可以自定义一个类实现com.opensymphony.xwork2.interceptor.Interceptor
            在这个接口中有三个方法  init  destory intercept, intercept方法是真正拦截的方法。

            在intercept方法中如果要向下继续执行,通过其参数ActionInvocation调用它的invoke()方法就可以。          

        2.声明一个Interceptor  
            在struts-default.xml文件中
            <interceptors>
                <interceptor name="" class=""/>
            </interceptors>
            注意:我们要自己声明一个interceptor可以在struts.xml文件中声明。

        3.在action中指定使用哪些拦截器.
            <interceptor-ref name="my"/>

        注意:只要显示声明使用了一个拦截器。那么默认的拦截器就不在加载。
-----------------------------------------------------------------------         
2.分析拦截器原理

    源代码执行流程:
        1.在StrutsPrepareAndExecuteFilter中查找
            在doFilter方法内有一句话 execute.executeAction (request, response, mapping) 执行Action操作.

        2.在executeAction执行过程中会访问Dispatcher类中的serviceAction,在这个方法中会创建一个
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
            这就是我们的Action的代理对象

        3.查看ActionInvocation,查看其实现类 DefaultActionInvocation.

            在其invoke方法中
            if (interceptors.hasNext()) {//判断是否有下一个拦截器.
                final InterceptorMapping interceptor = interceptors.next(); //得到一个拦截器
                String interceptorMsg = "interceptor: " + interceptor.getName();
                UtilTimerStack.push(interceptorMsg);
                try {
                        resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 
                        //调用得到的拦截器的拦截方法.将本类对象传递到了拦截器中。
                    }
                finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } 

            通过源代码分析,发现在DefaultActionInvocation中就是通过递归完成所有的拦截调用操作.


    关于interceptor与Filter区别:
        1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。
        2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
        3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
        4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
        5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。

-----------------------------------------------------------------------
3.案例

    权限控制:
        1.login.jsp------>LoginAction------------->book.jsp
                        登录成功,将用户存储到session。

        2.在book.jsp中提供crud链接。
            每一个连接访问一个BookAction中一个方法。

    要求:对于BookAction中的add,update,delete方法要求用户必须登录后才可以访问。search无要求。   

    怎样解决只控制action中某些方法的拦截?
        1.创建类不在实现Interceptor接口,而是继承其下的一个子类.MethodFilterInterceptor
            不用在重写intercept方法,而是重写 doIntercept方法。

        2.在struts.xml文件中声明
            <interceptors>
                <intercept name="" class="">
                    <param name="includeMethods">add,update,delete</param>
                    <param name="excludeMethods">search</param>
                </intercept>
            </interceptors>

===================================================================================================
3.struts2中文件上传与下载

1.上传
    浏览器端:
        1.method=post
        2.<input type="file" name="xx">
        3.encType="multipart/form-data";

    服务器端:
        commons-fileupload组件
        1.DiskFileItemFactory
        2.ServletFileUpload
        3.FileItem

    struts2中文件上传:
        默认情况下struts2框架使用的就是commons-fileupload组件.
        struts2它使用了一个interceptor帮助我们完成文件上传操作。
         <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>

    在action中怎样处理文件上传?
        页面上组件:<input type="file" name="upload">

        在action中要有三个属性:
            private File upload;
            private String uploadContentType;
            private String uploadFileName;

        在execute方法中使用commons-io包下的FileUtils完成文件复制.          
            FileUtils.copyFile(upload, new File("d:/upload",uploadFileName));

    ------------------------------------------------------------------------
    关于struts2中文件上传细节:
        1.关于控制文件上传大小
            在default.properties文件中定义了文件上传大小
            struts.multipart.maxSize=2097152 上传文件默认的总大小 2m

        2.在struts2中默认使用的是commons-fileupload进行文件上传。
            # struts.multipart.parser=cos
            # struts.multipart.parser=pell
            struts.multipart.parser=jakarta

            如果使用pell,cos进行文件上传,必须导入其jar包.

        3.如果出现问题,需要配置input视图,在页面上可以通过<s:actionerror>展示错误信息.
         问题:在页面上展示的信息,全是英文,要想展示中文,国际化

            struts-messages.properties 文件里预定义 上传错误信息,通过覆盖对应key 显示中文信息
            struts.messages.error.uploading=Error uploading: {0}
            struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3}
            struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
            struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}

            修改为
                struts.messages.error.uploading=上传错误: {0}
                struts.messages.error.file.too.large=上传文件太大: {0} "{1}" "{2}" {3}
                struts.messages.error.content.type.not.allowed=上传文件的类型不允许: {0} "{1}" "{2}" {3}
                struts.messages.error.file.extension.not.allowed=上传文件的后缀名不允许: {0} "{1}" "{2}" {3}

            {0}:<input type=“file” name=“uploadImage”>中name属性的值
            {1}:上传文件的真实名称
            {2}:上传文件保存到临时目录的名称
            {3}:上传文件的类型(对struts.messages.error.file.too.large是上传文件的大小)


        4.关于多文件上传时的每个上传文件大小控制以及上传文件类型控制.

            1.多文件上传
                服务器端:
                    只需要将action属性声明成List集合或数组就可以。

                    private List<File> upload;
                    private List<String> uploadContentType;
                    private List<String> uploadFileName;

            2.怎样控制每一个上传文件的大小以及上传文件的类型?
                在fileupload拦截器中,通过其属性进行控制.

                maximumSize---每一个上传文件大小
                allowedTypes--允许上传文件的mimeType类型.
                allowedExtensions--允许上传文件的后缀名.

                <interceptor-ref name="defaultStack">
                    <param name="fileUpload.allowedExtensions">txt,mp3,doc</param>
                </interceptor-ref>
----------------------------------------------------------------------------------------------              
2.下载
    文件下载方式:
        1.超连接
        2.服务器编码,通过流向客户端写回。

            1.通过response设置  response.setContentType(String mimetype);
            2.通过response设置  response.setHeader("Content-disposition;filename=xxx");
            3.通过response获取流,将要下载的信息写出。



    struts2中文件下载:       
        通过<result type="stream">完成。

        <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
        在StreamResult类中有三个属性:
              protected String contentType = "text/plain"; //用于设置下载文件的mimeType类型
              protected String contentDisposition = "inline";//用于设置进行下载操作以及下载文件的名称
              protected InputStream inputStream; //用于读取要下载的文件。

        在action类中定义一个方法
            public InputStream getInputStream() throws FileNotFoundException {
                FileInputStream fis = new FileInputStream("d:/upload/" + filename);
                return fis;
            }

        <result type="stream">
            <param name="contentType">text/plain</param>
            <param name="contentDisposition">attachment;filename=a.txt</param>
            <param name="inputStream">${inputStream}</param> 会调用当前action中的getInputStream方法。
        </result>


        问题1:<a href="${pageContext.request.contextPath}/download?filename=捕获.png">捕获.png</a>下载报错
            原因:超连接是get请求,并且下载的文件是中文名称,乱码。


        问题2:下载捕获文件时,文件名称就是a.txt ,下载文件后缀名是png,而我们在配置文件中规定就是txt?          
            <result type="stream">
                <param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 -->
                <param name="contentDisposition">attachment;filename=${downloadFileName}</param>
                <param name="inputStream">${inputStream}</param><!-- 调用当前action中的getInputStream()方法 -->
            </result>

    在struts2中进行下载时,如果使用<result type="stream">它有缺陷,例如:下载点击后,取消下载,服务器端会产生异常。
        在开发中,解决方案:可以下载一个struts2下载操作的插件,它解决了stream问题。

1.ognl与valueStack
ognl中有一个OgnlContext,它可以设置root与非root .root中数据获取时,不需要加#,而非root中数据在获取时,需要加上#.

重点:学习struts2中使用ognl时,最后要知道 谁是OgnlContext,谁是root,谁是非root.

-------------------------------------------------------------------------------------
1.ognl介绍
    OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
        * xwork 提供 OGNL表达式 
        * ognl-3.0.5.jar
    OGNL 是一种比EL 强大很多倍的语言 

    OGNL 提供五大类功能 
       1、支持对象方法调用,如xxx.doSomeSpecial(); 
       2、支持类静态的方法调用和值访问
       3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 )
       4、支持赋值操作和表达式串联
       5、操作集合对象。

    演示:在struts2中使用ognl表达式
        需要结合struts2的标签使用<s:property value="ognl表达式">

        <s:property value="'abc'.length()"/>  演示对象调用方法
        <s:property value="@java.lang.Math@max(10,20)"/> 演示静态成员访问.

        注意:在struts2中使用静态成员访问,必须设置一个常量:
                    struts.ognl.allowStaticMethodAccess=false

--------------------------
2.ValueStack
    它是一个接口com.opensymphony.xwork2.util.ValueStack。
    我们使用它是将其做为一个容器,用于携带action数据到页面.
    在在页面上通过ognl表达式获取数据。

=====================================================================
问题1:什么是valueStack?

    valueStack主要是将action数据携带到页面上,通过ognl获取数据

    1.ValueStack有一个实现类叫OgnlValueStack.
    2.每一个action都有一个ValueStack.(一个请求,一个request,一个action,一个valueStack) valueStack生命周期就是request生命周期。
    3.valueStack中存储了当前action对象以及其它常用web对象(request,session,application.parameters)
    4.struts2框架将valueStack以“struts.valueStack”为名存储到request域中。
---------------------------------------------
问题2:valueStack结构?

    ValueStack中 存在root属性 (CompoundRoot) 、 context 属性 (OgnlContext )
    * CompoundRoot 就是ArrayList
    * OgnlContext 就是 Map

    list集合中存储的是action相关信息
    map集合中存储的是相关映射信息,包含  paramters,request,session,application attr等。

    我们想要从list中获取数据,可以不使用#号.(它就是ognl的root)
    如果从map中获取数据,需要使用#. (其实在struts2中的map--context其实就是ognlContext)


    结论:
        ValueStack它有两部分 List  Map
        在struts2中List就是root   Map就是ognlContext.         
        默认情况下,在struts2中从valueStack获取数据从root中获取。

----------------------------------------------  
问题3:    值栈对象的创建 ,ValueStack 和 ActionContext 是什么关系 ?

     ActionContext ctx = ActionContext.getContext();
        if (ctx != null) {
            stack = ctx.getValueStack();
        }

    valueStack是每一次请求时,都会创建.
    在ActionContext中持有了valueStack的引用。

------------------------------------------------------- 
问题4:如何获得值栈对象?

    对于valueStack获取有两种方式:
        1.通过 request获取
            ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

        2.通过ActionContext获取.
            ValueStack vs=ActionContext.getContext().getValueStack();

---------------------------------------------------------
问题5:向值栈保存数据 (主要针对 root)

    主要有两个方法
        push(Object obj)------->底层就是 root.add(0,obj) 将数据存储到栈顶。
        set(String name,Object obj);----->底层是将数据封装到HashMap中,在将这个HashMap通过push存储。

    在jsp中 通过 <s:debug /> 查看值栈的内容    

------------------------------------------------------------
问题6: 在JSP中获取值栈的数据 
    root中数据不需要#,而context中数据需要#

    1.如果栈顶是一个Map集合,获取时,可以直接通过Map集合的key来获取value.
            <s:property  value="username"/>

    2.如果栈顶数据不是一个Map,没有key值,可以使用序号来获取。
        <s:property value="[0]">  从0的位置向下查找所有。

        <s:property value="[0].top"> 只查找0位置上数据。


    如果获取OgnlContext中数据:
        1.request数据    request.setAttribute() 
        2.session数据    session.setAttribute()
        3.application数据  application.setAttribute()
        4.attr   依次从request,session.application中查找
        5.parameters 获取请求参数

--------------------------------------------------------------  
ValueStack主流应用:就是解决将action数据携带到jsp页面。

    问题:action向jsp携带数据,都是什么样的数据?
        1.文本(字符串)
            1.fieldError   校验数据错误信息提示
            2.actionError 关于逻辑操作时错误信息(例如登录失败)
            3.message 就是一个信息.

            this.addFieldError("msg", "字段错误信息");
            this.addActionError("Action全局错误信息");
            this.addActionMessage("Action的消息信息");

            * fieldError 针对某一个字段错误信息 (常用于表单校验)、actionError (普通错误信息,不针对某一个字段 登陆失败)、 actionMessage 通用消息   

            在jsp中使用 struts2提供标签 显示消息信息
                <s:fielderror fieldName="msg"/>
                <s:actionerror/>
                <s:actionmessage/>

        2.复杂数据
            可以使用valueStack存储.

            在action中存储数据:
                List<User> users = new ArrayList<User>();
                users.add(new User("tom", "123", 20, "男"));
                users.add(new User("james", "456", 21, "男"));
                users.add(new User("fox", "789", 26, "男"));

                vs.push(users);

            在页面上获取数据:
                使用了<s:iterator>标签来迭代集合。
                <s:iterator value="[0].top" var="user"> 这是将集合中迭代出来每一个元素起个名称叫user,而user是存储在context中,不在root中.l

                <s:iterator value="[0].top" var="user">
                    username:<s:property value="#user.username"/><br>
                    password:<s:property value="#user.password"/>
                    <hr>
                </s:iterator>

                注意:如果我们在使用<s:iterator>进行迭代时,没有给迭代出的元素起名.
                <s:iterator value="[0].top">
                    username:<s:property value="username"/><br>
                    password:<s:property value="password"/>
                    <hr>
                </s:iterator>
        ---------------------------------------------------
        关于默认压入到valueStack中的数据.
            1.访问的action对象会被压入到valueStack中.
                DefaultActionInvocation 的 init方法 stack.push(action);

                * Action如果想传递数据给 JSP,只有将数据保存到成员变量,并且提供get方法就可以了 

            2.ModelDriveInterceptor会执行下面操作
                    ModelDriven modelDriven = (ModelDriven) action;
                    ValueStack stack = invocation.getStack();
                    Object model = modelDriven.getModel();
                    if (model !=  null) {
                        stack.push(model);
                    }
                    将实现了ModelDrive接口的action中getModel方法的返回值,也就是我们所说的model对象压入到了
                    valueStack.

--------------------------------------------------------------------------
    问题7:为什么el表达式可以访问valueStack中数据?

        struts2框架中所使用的request对象,是增强后的request对象。

        ${username}---->request.getAttribute("username");  

        增强后的request,会首先在request域范围查找,如果查找不到,会在valueStack中查找。

        StrutsPreparedAndExecuteFilter的doFilter代码中 request = prepare.wrapRequest(request);  
        * 对Request对象进行了包装 ,StrutsRequestWrapper 
        * 重写request的 getAttribute 
            Object attribute = super.getAttribute(s);
            if (attribute == null) {
               attribute = stack.findValue(s);
            }
          访问request范围的数据时,如果数据找不到,去值栈中找 
            ?   request对象 具备访问值栈数据的能力 (查找root的数据)

--------------------------------------------------------------------------------
OGNL表达式常见使用($ % #)

    1.#号
        用法一  # 代表 ActionContext.getContext() 上下文
          <s:property value="#request.name" />  ------------>  ActionContext().getContext().getRequest().get("name");
          #request
          #session
          #application
          #attr
          #parameters 
        用法二 : 不写# 默认在 值栈中root中进行查找 
           <s:property value="name" /> 在root中查找name属性 
            * 查询元素时,从root的栈顶元素 开始查找, 如果访问指定栈中元素   
                <s:property value="[1].name" />  访问栈中第二个元素name属性 
            * 访问第二个元素对象 <s:property value="[1].top" />

        用法三 :进行投影映射 (结合复杂对象遍历 )
               1)集合的投影(只输出部分属性
                    <h1>遍历集合只要name属性</h1>
                    <s:iterator value="products.{name}" var="pname"> 
                    <s:property value="#pname"/>
                    </s:iterator>
               2)遍历时,对数据设置条件 
                <h1>遍历集合只要price大于1500商品</h1>
                <s:iterator value="products.{?#this.price>1500}" var="product"> 
                    <s:property value="#product.name"/> --- <s:property value="#product.price"/>    
                </s:iterator>
               3)综合
               <h1>只显示价格大于1500 商品名称</h1>
                <s:iterator value="products.{?#this.price>1500}.{name}" var="pname"> 
                    <s:property value="#pname"/>
                </s:iterator>   

        用法四: 使用#构造map集合 
            经常结合 struts2 标签用来生成 select、checkbox、radio
            <h1>使用#构造map集合 遍历</h1>
            <s:iterator value="#{'name':'aaa','age':'20', 'hobby':'sport' }" var="entry">
                key : <s:property value="#entry.key"/> , value:  <s:property value="#entry.value"/> <br/>
            </s:iterator>

    --------------------------------------------------------    
    2.%号
        %作用:就是用于设定当前是否要解析其为 ognl表达式.

        %{表达式}  当前表达式会被做为ognl解析.
        %{'表达式'} 当前表达式不会被做为ognl解析。

        <s:property value="表达式"> 对于s:property标签,它的value属性会被默认做为ognl.

        以后,所有表达式如果想要让其是ognl  %{表达式}
    ----------------------------------------------------------  
    3.$号
        $作用:就是在配置文件中使用ognl表达式来获取valueStack中数据.

        1.struts.xml
            <result type="stream">
                <param name="contentType">${contentType}</param>
            </result>

        2.在校验文件中使用
            ${min}  ${max}
            ${minLength} ${maxLength}

        3.在国际化文件中使用

            在properties文件中
                username=${#request.username}
            在jsp页面
                <s:text name="username">

    -----------------------------------------------------------------
    总结: #就是用于获取数据  %就是用于设置是否是ognl表达式  $就是在配置文件中使用ognl. 

==============================================================================================================
2.防止表单重复提交
问题:什么是表单重复提交?
regist.jsp—–>RegistServlet
表单重复提交 危害: 刷票、 重复注册、带来服务器访问压力(拒绝服务)

解决方案:
    在页面上生成一个令牌(就是一个随机字符串),将其存储到session中,并在表单中携带.
    在服务器端,获取数据时,也将令牌获取,将它与session中存储的token对比,没问题,
    将session中令牌删除。

struts2中怎样解决表单重复提交:
    在struts2中解决表单重复提交,可以使用它定义的一个interceptor。
     <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>

    步骤:
        1.在页面上需要使用一个token tag
            在表单中添加一个标签  <s:token/>

            就会在页面上生成一个令牌,并存在于表单中。

        2.需要在action中引入token拦截器
            <interceptor-ref name="token"/>

        3.需要配置视图
            <result name="invalid.token">/token.jsp</result>

     通过 <s:actionError/> 显示错误信息 
     覆盖重复提交信息  struts.messages.invalid.token=您已经重复提交表单,请刷新后重试

===============================================================================================
3。struts2中json插件使用

1.struts2中怎样处理异步提交(ajax)

    原始:
    HttpServletResponse response = ServletActionContext.getResponse();
    response.getWriter().write("hello " + msg);
    response.getWriter().close();

    还可以使用struts2中提供的json插件:

        1.导入json插件包
            在struts2的lib包下  struts2-json-plugin-2.3.15.1.jar。

        2.在struts.xml文件中配置
            1.<package extends="json-default">
            2.设置视图<result type="json">

        这样设置后,会将valueStack栈顶数据变成json。
        对于我们的程序,也就是会将action对象转换成json。


        <param name="root">p</param> 如果没有设置,可以理解成将整个action都转换成json的数据。也就是
         在action中提供的getXxx方法,就是json中的一个属性。

        如果设置了root,那么,只将指定数据转换成json.

    --------------------------------------------------------
    怎样设置转换成json的对象中不包含特定的属性?

        1. @JSON(serialize=false) 在getXxx方法上设置 
        2. 还可以通过json插件的interceptor完成.
            <param name="includeProperties">ps\[\d+\]\.name,ps\[\d+\]\.price,ps\[\d+\]\.count</param>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值