Struts总结

来源:互联网 发布:平面设计需要什么软件 编辑:程序博客网 时间:2024/05/19 18:41
Struts
使用Filter作为控制器的MVC         POJO普通的一个java类
模型:封装应用程序的数据和业务逻辑  POJO
视图:实现应用程序的信息显示功能 JSP
控制器:接受来自用户的输入,调用模型层,响应对应的视图组件 Servlet Filter

使用Filter 作为控制器可以方便的在应用程序里对所有资源的进行控制访问
Servlet VS Filter
servlet能做的Filter也能做
但是Filter可以拦截资源servlet缺是不行

eg:在一个form表单中action="input.action"  action中的参数就是一个servletPath
在自定义的filter中继承 filter  在doFilter中 通过request对象获取servletPath
然后只要进行一个判断字符串是否相等 相等就执行响应的内容 并进行跳转之类的动作
跳转后要有一个 return; 因为在filter中 有个chain.doFilter(); 这个方法会进入到下一个过滤器中过滤 如果没有return 的话会抛异常


Struts2是一个用来开发MVC应用程序的框架,它提供了一些web应用程序开发过程中一些常见的解决方案
对来自用户输入的数据进行合法性验证
统一的布局
可扩展性
国际化和本地化
支持Ajax
表单的重复提交
支持的文件下载

搭建Struts2的环境
1.加jar包 struts\apps\struts2-blank\WEB-INF\lib下多有的jar包
2.在web.xml文件中配置struts2  复制 struts\apps\struts2-blank\WEB-INF\web.xml文件中的过滤器的配置到当前的web.xml 文件中
3.在web应用的classPath下添加 struts2的配置文件 struts.xml 复制struts\apps\struts2-blank\WEB-INF\classes下的struts.xml 到当前的web应用的src目录下

添加DTD提示  http://struts.apache.org/dtds/struts-2.3.dtd  选择的key type 是uri

在struts.xml下的配置
    配置Struts可以受理的请求的扩展名
    <constant name="struts.action.extension" value="action,do,"> 表示servletPath的扩展名可以使.action .do 或者没有扩展名
    关于Struts2请求扩展名的问题
    >org.apache.struts2包下的default.properties 中配置了Struts2应用的常量
    >struts.action.extension 定义当前Struts2应用可以接受的请求的扩展名
    >可以在Struts.xml中一常量配‘置的方式修改default.properties 所配置的常量
    
    <constant name="struts.configuration.xml.reload" value="true"></constant>  <!-- 这是配置struts修改了配置不用重启 -->
    
    package: 包 struts 通过使用package来组织模块
    name属性:必须 用于其它的包应用当前的包
    class属性:默认值com.opensymphony.xwork2.ActionSupport
                nnmethod:默认值 execute
    extends: 当前包继承哪个包,可以继承其中的所有配置 通常情况下继承 struts-default
    extends:struts-default  struts-default  这个包 必须是要继承才能使用
    namespace: 是可选的 默认值为"/"
           若有一个非默认值 则要想要调用这个包中的Action,就必须把这个属性所定义的命名空间添加到有关的uri字符串中
        http://localhost:8080/contextPath/namespace/actionName.action          
    <package name="hello" extends="struts-default">
        <action name="prodect-input">  这个name是Action的虚拟路径   
        一个action 可以有多个result子节点  用name区分 success是默认的值 name是Action类中方法的返回值  
        type属性是结果类型默认值为dispatcher(内部跳转) redirect(重定向)-不好用
            <result name="success">/WEB-INF/pages/input.jsp</result>
        </action>
    </package>
   
struts2  vs  自实现
1.要搭建环境
2.不需要显示的定义Filter 而使用的是Struts2配置文件
3.以前写el表达式的页面变得简单了

Action
action  VS   Action
    action :代表的是一个action struts2请求
    Action 类代表能够处理Struts请求的类 。 eg:在实体类中写着一个处理struts2请求的方法
Action 类  属性的名字必须要遵守javaBeans 属性名相同的命名规范 ,属性的类型可以使任意类型,从字符串到非字符串之间的数据转换可以自动发生
           必须要有一个无参的构造方法
       至少有一个供struts在执行这个action时调用的方法
       同一个Action类可以包含多个action方法
       struts2会为每一个http请求创建一个新的Action实例

在Action中如何访问Web资源
1.什么是web资源
    HttpServletrequest,HttpSession,ServletContext等原生的servlet API
2.为什么访问web资源
    B\S的应用的Controller 必然要
3.如何访问
  1.和servletAPI解耦的方式:只能访问到有限的servletAPI对象,且只能访问到有限的方法(读取请求参数,读取域对象的属性,使session失效...)
      > 使用ActionContext
    //先获取ActionContext对象  这个对象是Action的上下文对象  可以冲从中获取到当前Action需要的一切信息
    ActionContext actionContext = ActionContext.getContext();
        
    //1.获取application对应的Map ,可以通过map获取属性 也可以设置
    Map<String, Object>  applicationMap = actionContext.getApplication();
    applicationMap.put("applicationKey", "applicationValue");
        
    //2.session
    Map<String, Object> sessionMap = actionContext.getSession();
    sessionMap.put("sessionKey", "sessionValue");
        
    //3.request
    //actionContext中没有提供getRequest 方法来获取request对应的Map 需要手动的调用 get("request")方法 来获取
    Map<String, Object> requestMap = (Map<String, Object>) actionContext.get("request");
    requestMap.put("requestKey", "requestValue");
    //4.获取请求参数对应的Map,并获取指定的参数值
    //键:请求参数的名字              值:请求参数对应的字符串数组      
    //注意:1.getParameters 的返回值为 Map<String, Object> 而不是 Map<String,String[]>
    //    2.parameter这个map只能读不能写入数据           如果写入不会报错,也不起作用     只有uri上转发来的才能获取到
    Map<String, Object> parameters = actionContext.getParameters();
    System.out.println(((String[])parameters.get("name"))[0]);
      
      > 实现XxxAware接口   ApplicationAware,SessionAware,RequestAware,ParameterAware
     实现ApplicationAware接口 重写set方法 自己写一个属性  这是一种依赖注入 setApplication方法是struts2调用的
    public String execute(){
        //向application中添加一个 属性  applicationKey2  applicationValue2
        applicationMap.put("applicationKey2", "applicationValue2");
        //从 application中获取一个属性
        System.out.println(applicationMap.get("date"));
        return "success";
    }
    private Map<String, Object> applicationMap;
    public void setApplication(Map<String, Object> applicationMap) {
        this.applicationMap = applicationMap;
    }


    选用的建议 :若一个Action类中有多个action方法,且多个方法都要使用域对象的map或parameters 则建议使用实现Aware接口的方式
    注意session对应的Map 实际上是SessionMap类型的 强转后调用invalidate() 方法,可以使session失效
  2.和servletAPI耦合的方式:可以访问更多的ServletAPI对象,且可以调用其原生的方法
      > 使用ServletActionContext

    HttpServletRequest request = ServletActionContext.getRequest();
    HttpSession session = ServletActionContext.getRequest().getSession();
    ServletContext servletContext = ServletActionContext.getServletContext();

      > 实现ServletXxxAware接口
        可以由struts2注入 需要的servlet需要的相关对象
    ServletRequestAware, 注入ServletRequest对象
    ServletContextAware,  注入ServletContext对象
    SerlvetResponseAware  注入SerlvetResponse对象 很少使用
    
ActionSupport
com.opensymphony.xwork2.ActionSupport类 是默认的Action类
 在写一个action节点的时候 不写 class默认就是它
 在手工完成字段验证,显示错误信息,国际化等情况下推荐继承ActionSupport

Result
    是action节点的子节点
    代表action方法执行后,可能去的一个目的地
    一个action节点可以配置多个result子节点
    result的name属性的值对应着action方法可能有的一个返回值
  result中的type属性 表示结果的响应类型
      type属性的值在struts-default包的result-types节点的name属性中定义
      常用的有
        >dispatcher(默认的):转发 同servlet中的转发是一样的
    
    >redirect: 重定向  可以重定向到页面 也可以是action
    
    >redirectAction: 重定向到action
    <result name="" type="redirectAction">
        <param name="actionName">TestAction</param>
        <param name="namespace"></param>
    </result>
    
    <result name="" type="redirect">TestAction.action</result>
    用redirect也可以重定向到action
    
    >chain:  转发到action  
    <result name="" type="chain">
        <param name="actionName">TestAction</param>
        <param name="namespace"></param>
    </result>
    <result name="" >TestAction.action</result>这个是不行的
    不能通过一个dispatcher的方式转发到一个action

动态的配置 uri上有!标识
<a href="userAction!addUser">添加</a>
在struts配置文件中 不再需要指定的方法 !后的addUser已经指定了方法
    会动态的调用到action类中的方法  
    
通配符映射
<action name="*-*" class="com.zr.action.{1}Action" method="{2}">
    <result name="{2}">/{2}.jsp</result>
</action>
<action name="UserAction-*" class="" method="{1}">
    <result name="{1}-success">/{1}.jsp</result>
</action>
*表示一端字符串 {1} 表示是* 代表的这段字符串
通配符映射的规则
    >若找到多个匹配,没有通配符的胜出 是要找精确的那个
    >若指定的动作不存在,Struts将会尝试把这个URI与任何一个包含通配符* 的动作名进行匹配
    >被通配符匹配到的URI字符串的子串可以用{1},{2}来引用
    >{0} 匹配整个URI
    >若Struts找到带有通配符的匹配不止一个, 则按先后顺序匹配

全局结果集  这个几点必须在action节点前  是有顺序的
<global-results>
    <result></result>
</global-results>



关于值栈  是在Struts2的过滤器中创建出来的 每经过一次过滤器 都会创建一个新的valueStack
1.在第一个struts的项目中${prodectName} 读取prodectName值,实际上该属性并不存在request等域对象的中,而是从值栈中获取
2.ValueStack
>可以从ActionContext中获取值栈对象
>值栈分为两个逻辑部分
    >Map栈:实际上是OglnContext类型,是一个Map,也是对ActionContext的一个引用,里边保存着各种Map
    requestMap ,sessionMap,applicationMap,parameterMap,attr
    
    >对象栈:实际上是CompoundRoot类型,是一个使用ArrayList定义的栈,里边保存各种和当前Action实例相关的对象,
    是一个数据结构意义的栈。

OGNL
Ognl.getValue(String expression,Map context,Object root);
从ognl中取值的方法
三个参数的时候如果取的是map中的值 要表达式中要加#  取的是object中的时候不用加 直接属性名
如果是两个参数的时候
Ognl.getValue(String expression,?)  ?这个位置上可以是一个map类型 也可以是一个object类型
在这个方法中取map中的值的时候表达式中不用加# 因为这时候map已经作为了 根对象
其实 跟对象就是一个键值对  (传三个参数的时候 感觉就和根对象放入了一个map里)

struts2利用s:property 标签和OGNL表达式来读取值栈中的属性值
   值栈中的属性值:
     > 对于对象栈:对象栈中某一个对象的属性值

     > Map栈:request,session,application的一个属性值或一个请求参数的值
   
   >读取对象栈中对象的属性:
     若想访问Object Stack 里某个对象的属性,可以使用以下几种形式之一:
         object.propertyName; object['propertyName'];object["propertyName"]
     Object Stack里的对象可以通过一个从零开始的下标来引用。Object Stack里的栈顶对象可以用[0]来引用
        它下面的那个对象可以用[1]引用
        eg:[0].message;
     [n]的含义是从第n个对象开始搜索,而不是只搜索第n个对象
     若从栈顶对象开始搜索,则可以省略下标的部分:message
     结合s:property 标签:<s:property value="[0].message"/>  等同于 <s:property value="message"/>
在jsp页面上<%@ taglib prefix="s" uri="/struts-tags" %>
    
   >读取Map栈中的属性值 可以使用一下几种形式
    #object.propertyName; #object['propertyName']; #object["propertyName"]
  可以利用OGNL调用
    任何一个java类的静态字段和方法
    被压入到ValueStack栈对象上的公共字段和方法
     在默认情况下Struts2不允许调用java类的静态方法 需要重新设置 struts.ohnl.allowStaticMethodAccess 标记变量为 true
     <constant name="struts.ohnl.allowStaticMethodAccess" value="true"></constant>
     调用静态字段或方法需要使用一下的语法
        @fullQualifiedClassName@fieldNames
    @fullQualifiedClassName@methodName(argumentList);
     调用一个实例字段或方法的语法 , 其中object是Object Stack栈里的某个对象的引用
        .object.fieldName
    .object.methodName(argumentList)
    <s:property value="setProductName('aaa')" />
    <s:property value="productName"/>
     访问数组类型的属性
        可以使用下标访问数组中指定的元素:color[0]
    可以通过调用其length字段查出给定数组中有多少个元素 color.length
     访问List类型的属性
       与数组的用法类似
       可以使用ognl表达式创建一个List,创建一个List与声明一个java数组是相同的{"red","green","blue"}
     访问Map类型的属性
       希望获取map中的某个值时:map[key]  
       size或size() 可以获取map中的键值对个数
       isEmpty ,isEmpty()
       可以使用以下的方法创建一个map
       #{key1:value1,key2:value2...}


声明式异常处理
exception-mapping元素 :配置当前action的声明式异常  配置在action节点中result之前
有两个属性 result:指定一个响应结果,该结果将在捕获到指定异常时执行,既可以来自当前的action声明,也可以来自global-results声明
           exception:指定要捕获的异常
可以通过 global-exception-mappings元素为应用程序提供一个全局性的异常捕获映射,但在global-exception-mappings元素下声明的任何exception-mapping元素
只能引用在global-results元素下声明的某个result元素
    eg:<global-results>     global-results元素必须要在global-exception-mappings这个元素的之前
            <result name="input"></result>
        </global-results>
        <global-exception-mappings>
        <exception-mapping result="input" exception="异常的全类名"></exception-mapping>
        </global-exception-mappings>
在异常发生的时候会在ValueStack中添加两个对象
    exception
    exceptionStack
    可以在视图上通过<s:property>标签显示异常信息

Struts2自动的把Action对象放入到值栈中
    放入的时间点为:struts2终将调用Action类的Action方法。但在调用该方法之前:
      1.先创建了一个叫StrutsActionProxy 对象,
      2.在创建StrutsActionProxy 之后,对其进行初始化时候,把Action对象放入了值栈中

通用标签
   > s:property  打印值栈中的属性的值
       对于对象栈,打印值栈中对象的属性值
       对应Map栈,打印request,session,application 的某个属性值或某个请求参数的值
    在许多情况下如果只是打印的话  用EL的话更简单点

   > s:url标签 常用的属性 action   method  namespace  value   var
    eg:<s:url value="/getProduct" var="url">
            <s:param>元素下的value属性中的值 会自动ognl 解析 也就是在值栈中找到对应的值
       <s:param name="productId" value="productId"></s:param>
       </s:url>
       ${url } 这个EL表达式可以输出 对应的 contentPath/getProduct?productId=id (id为解析出来的值)
    
    <s:url value="/getProduct" var="url">
            <s:param>元素下的value属性中的值 如果不希望被ognl解析 加单引号
       <s:param name="productId" value="'abc'"></s:param>
       </s:url>
    构建一个请求action的地址
    <s:url action="testAction" namespace="/hello" method="save" var="url"></s:url>
    获取到请求参数的值
    <s:url value="/getProduct" var="url" includeParams="all"></s:url>
   
   >s:set
       set标签用来在以下Map对象里创建一个键值对
            ValueStack值栈的ContextMap值栈
        Map类型的session对象
        Map类型的application对象
        Map类型的request对象
        Map类型的page对象
    对value属性值自动的进行OGNL解析
    <s:set name="productName" value="Intel" scope="request"></s:set>
   
   >s:push 把一个对象在标签开始是压入值栈,标签结束的时候弹出值栈。
    <s:push value="">
        在这里写压入值栈的值
    </s:push>

   >s:if s:else和s:elseif 标签  test的值也是可以使值栈解析获取到price的值
    <s:if test="price>1000">
        ....
    </s:if>
    <s:elseif test="price>800">
        ....
    </s:elseif>
    <s:else>
        ....
    </s:else>

   >s:iterator 用来遍历一个数组,collection,map 并把这个可遍历的对象里的每一个元素一次压入和弹出值栈
    <s:iterator value="#request.persons">  如果persons这个集合在对象栈中 value=persons 就行
    ${name }  ${age}  //这里直接通过EL获取person对象中的属性
    </s:iterator>
    加另一个属性 status="status" 可以在标签内 写入
    ${status.index }  和${status.count  }  这两个是一个序列 index是从0开始 count是从1开始
   
   >s:sort 进行排序的
   <s:sort comparator="#request.comparator" source="" var="persons"></s:sort>
   comparator的值时一个实现了comparator接口的类

   >s:date
   <s:date name="#session.date" format="yyyy-mm-dd" var="date2"/>
   ${date2}

   >s:a  %{} 表示强制进行ognl解析
    <s:a href="psersonAction.action?name=%{name }">${name}</s:a>

表单标签
使用表单标签的优点
    >表单会显
    >对页面进行布局和排版
    和html中的form差不多
    s:form 会自动进行排版
 <s:form action="" method="post(默认值)">  
     <s:hidden name="userId"></s:hidden>
     <s:textfield name="userName" label="UserName"></s:textfield>
     <s:password name="pasword" label="Password"></s:password>
     <s:textarea name="desc" label="Desc"></s:textarea>
     <s:submit></s:submit>
 </s:form>
     
     s:form是如何回显的  
      是把栈顶对象的属性直接显示出来的, 若栈顶没有对应的值 向下查找匹配
 <s:checkbox> 用于提交一个布尔值 并不是 html表单标签中的多选框
   <s:checkbox name="married" label="婚否"></s:checkbox>  相对于html中的checkbox隐藏了一个hidden标签
    在html的checkbox中如果没有勾选是不会提交的 而 struts中的checkbox 由于有了这个隐藏的标签 没有提交的时候会提交一个false

对于struts下的radio select checkboxlist 的几个重要的属性 list listKey listValue
  <s:radio name="sex" list="#{'1':'man','2':'woman'}" label="性别"></s:radio>
  listKey  是city类中的cityId属性名  listValue 也是对应city类中的属性
  <s:checkboxlist name="city" list="#request.citys" listKey="cityId" listValue="cityName" label="City"></s:checkboxlist>
   在使用checkboxlist时 服务端需要使用list类型 以保证数据能够正常的回显
radio  checkboxlist  list属性的值 要以#开头
Struts2 主题
    simple 把标签翻译成最简单的HTML对应的元素
    xhtml 是默认的主题
    css_xhtml 与默认的相似 这个可以进行css进行排版
    ajax 增加了一些ajax的功能
    通过theme="" 来修改主题 可以为整个表单 或者某一个元素
    在page,request,session,application 中添加一个theme属性
    修改Struts.properties文件中的struts.ui.theme 属性

Struts2 CRUD操作中的一些拦截器

Params拦截器  
          这个拦截器讲把表单中字段映射到ValueStack栈的栈顶对象的各个属性,如果某个字段在模型里边没有匹配的属性
          再匹配值栈中的下一个对象
ModelDriven拦截器   把action和model隔离开  (在做save操作时候)
          在action类中实现ModelDriven<object>接口,重写getModel方法 后  将把返回的对象放置于 值栈的栈顶    
           实现接口后  在action类中 加上一个object作为action中的一个属性
       重写getModel方法
       public Object getModel(){
        obj = new Object();
        return obj;
       }
       在struts-default.xml 文件中的默认的拦截器栈 ModelDriven拦截器在Params拦截器之前执行
       所以在这么写后执行 在页面上 栈顶的队形已经变为object对象 而不是action对象
运行流程
    先回执行ModelDrivenIntercptor中的intercept
       首先先获取action对象,此时的action对象应该是已经实现了ModelDriven拦截器的
       再判断action是不是ModelDriven的实例
       是的话 强制转换为 ModelDriven类型
       获取值栈
       调用ModelDriven接口的getModel()
       再把返回的对象置于栈顶
    执行ParametersInterceptor 的 intercept 方法:把请求参数的值赋给栈顶对象对应的属性,若栈顶对象没有对应的属性,则查询
      值栈中下一个对象对应的属性
    注意 :
     getModel 不能提供一下实现 这样写 当前action的成员变量 obj 却是null;
       public object getModel(){
           return new Object();    
       }

在做update的时候
     从数据库中根据id获取到的对象赋给action类中的成员变量(原来是栈顶对象) 经过赋值后就不是栈顶对象了,必须要重新压栈
     eg: employee = dao.get(employee.getEmployeeId()); 这样是不能实现回显的
     可以这样 ActionContext.getContext().getValueStack().push(dao.get(employee.getEmployeeId()));  
        这样写的话浪费内存 因为这样压栈的话前两个对象都是 employee对象 第一个对象属性值都有  第二个只有id
     合理的方法是在getModel()中进行一个判断  判断是save 还是update
     若是save 则 object = new Object();
     若是update 则 object = dao.get(object.objectId());
     这样的话只能通过判断时候 有id 来判断是哪一个操作
      所以就还需要给id 复制 那么 就需要经过 params拦截器
      这时候有struts提供了一种paramsPrepareParamsStack 拦截器栈实现   那么就需要在struts.xml 中配置这个拦截器栈
         在package节点下<default-interceptor-ref name="paramsPrepareParamsStack "></default-interceptor-ref>
      在这个拦截器中     先进行 params拦截器 然后是 ModelDriven拦截器 最后是params 拦截器
    public object getModel(){
           if(objectId==null){
        object = new Object();
       }else{
        object = dao.get(object.objectId());
       }
       return object;
       }
    
使用paramsPrepareParamsStack拦截器栈后的流程  paramsPrepareParamsStack和defaultStack 都是拦截器栈  默认使用的是后者
    paramsPrepareParamsStack 拦截器栈
      用法在与 params-> modelDriven->params
      所以可以先把请求参数赋给action类对应的属性,再根据赋给action的那个属性值决定压到值栈栈顶的对象,最后再为栈顶对象的属性赋值
问题:在执行delete操作的时候 id不为null getModel却加载了一个对象,不该加载
      在执行list 方法的时候 由于object 为 null 所以 getModel也会加载 一个空的对象 浪费空间
   解决方案 :使用 PrepareInterceptor和Prepareable 接口
     给action类 实现 Prepareable 接口 重写prepare()方法
     prepare方法主要是用来为getModel方法准备model的
     
       关于 PrepareInterceptor
        首先获取Action实例
    判断是否实现了Prepareable接口
    根据当前拦截器的firstCallPrepareDo(默认为false) 属性确定prefixes数组
    若为false prefixes[prepare,prepareDo]    
    再调用前缀方法  PrefixMethodInvocationUtil.invokePrefixMethod(invocation,prefixes);
        根据当前拦截器的alwaysInvokePrepare(默认为true) 决定是否调用Action类中的prepare方法       
        
    PrefixMethodInvocationUtil.invokePrefixMethod(invocation,prefixes); 这个方法
      获取action实例
      获取要调用action方法的名字
      获取前缀方法  getPrefixedMethod(prefixes,methodName,action);
      若方法不为空,则通过反射调用前缀方法
       
       PrefixMethodInvocationUtil.getPrefixedMethod(prefixes,methodName,action); 这个方法
          先把方法的首字母变为大写
      遍历前缀数组  通过拼接的方式 得到前缀方法名
      利用反射从action中获取从action中获取对应的方法 ,若有则直接返回 并结束循环
 若 action实现了Prepareable接口, 则Struts将尝试执行prepare[ActionMethodName]方法,
     如果prepare[ActionMethodName]方法不存在,则将尝试执行prepareDo[ActionMethodName]方法
     若都不存在就不执行了
若PrepareInterceptor 的alwaysInvokePrepare属性为false,则struts2将不会调用实现了Prepareable接口的Action的prepare()方法

那么 问题的解决方法
    可以为每一个ActionMethod准备prepare[ActionMethodName]方法,而抛弃原来的prepare()方法  就是在有些方法下 给出一个prepareSave()或prepareUpdate()等方法
    将PrepareInterceptor的alwaysInvokePrepare属性置为false,避免调用prepare() 方法
    如何在配置文件中为 PrepareInterceptor 拦截器栈的alwaysInvokePrepare属性置为false
    
    <interceptors>
        <interceptor-stack name="myStack">
        <interceptor-ref name="paramsPrepareParamsStack">
            <param name="prepare.alwaysInvokePrepare">false</param>
        </interceptor-ref>
        </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="myStack"/>


拦截器只能完成字符串和基本数据类型的转换
类型转换
    类型转换出错的时候转到哪个页面
    显示错误的信息
类型转换错误
        若Action类没有实现ValidationAware接口:继续调用其方法,好像什么都没有发生
    若Action实现了ValidationAware接口:不会在继续调用action的方法,检查相关action元素的声明是否包含着一个name=input的result
       如果有把控制权交给result元素 ,如果没有就抛出一个异常
        通常是继承ActionSupport的方式 间接的实现 ValidationAware接口
如何覆盖默认的错误消息?
    1.在对应的Action类所在的包中新建 ActionClassName.preperties文件,ActionClassName即为包含着输入字段的Action类的全类名
    2.在属性文件中添加键值对:invalid.fieldvalue.fieldName=xxx    xxx表示自定义的错误消息
如果是simple主题,还会自动的显示错误消息吗?如果不会显示,该怎么办?
        simple主题不能显示
    怎么显示呢 ?
       1.通过debug标签 可以知道在值栈的Action(实现了ValidationAware接口)对象中有一个fieldErrors属性,该属性的类型为
         Map<String,List<String>> 键:字段名(属性名)  值:错误消息组成的List 所以可以使用EL或者OGNL的方式 显示错误
         消息  eg:${fieldErrors.age[0] }
       2.还可以使用s:fielderror 标签来显示。可以通过fieldName属性显示指定字段的错误
       3.若是simple主题,并且使用了<s:fielderror >标签来显示错误信息,则该信息在一个 ul,li,span 中,如何除去ul,li,span呢
         在template.simple 下面的fielderror.ftl 定义了simple主题下,s:fielderror 标签显示错误信息的样式。
         所以修改该配置文件  ,  在src下新建template.simple 包,新建fielderror.ftl 文件 把原生的复制进来 剔除ul,li,span
如何定义自己类型转换器?
为什么需要自定义呢?struts不能完成字符串到引用类型的转换
如何定义呢?
    1.开发类型转换器的类 :继承StrutsTypeConverter 类
    2.配置类型转换器
      两种方式
      >基于字段的配置  局部转换器
        在字段所在的Model(可能是Action或JavaBean)的包下,新建一个ModelClassName-converter.preperties文件
        在该文件中输入键值对:fieldName=类型转换器的全类名。
        第一次使用该转换器创建一个实例
      >基于类型的配置 全局的转换器
        在src下新建xwork-conversion.preperties
        在文件中输入待转换的类型=转换类型的全类名
        在应用加载的时候创建了两个实例
   扩展StrutsTypeConverter 类
   自定义一个类 继承StrutsTypeConverter
   public class MyConverter extends StrutsTypeConverter{
    private DateFormat dateFormat;
    public MyConverter(){
       //dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
       类型转换不要放在构造方法中 因为在基于类型的配置时候给转换器创建实例的时候是在struts应用加载之前
       如果 时间的格式写在web.xml文件中 那么基于类型的配置的时间的格式是获取不到的  应用这个类实例化是在应用加载前
       
    }
    public DateFormat getDateFormat(){
        if(dateFormat == null){
        dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        }
        return dateFormat;
    }
    public Object convertFromString(Map context,String[] values,class toclass){
        if(toclass == Date.class){
        if(values!=null&&values.length>0){
            String value= values[0];
            return getDateFormat().parseObject(value);
        }
        }
        return values;
    }
    public String convertToString(Map context,Object o){
        if(o instanceof Date){
        Date date = (Date)o;
        return getDateFormat().format(date);
        }
        return null;
    }
   }

类型转换 与 复杂属性的配合使用
例子 : 两个 Model Department(id,deptname,mgr) 和 Manager(name,date)
1.在页面deptname可以直接录入 那么 mgr的属性 该如何录入呢 ?
     struts2的表单标签的name属性可以被赋为属性的属性name=mgr.name   name=mgr.date
2.mgr中有一个Date类型的date ,Struts2可以进行自动转换吗 ?
全局的类型转换器 还是可以正常工作的


Struts2的国际化
1.如何配置国际化资源文件
  >Action 范围资源文件:在Action类文件所在的路径简历名为
   ActionName_language_country.properties的文件
  >包范围资源文件:在包的根路径下建立文件名为
   package_language_country.properties 的属性文件
   一旦建立,处于该包下的所有Action都可以访问该资源文件。
   注意:包范围资源文件的 baseName 及时package 不是Action所在的包名
  >全局资源文件
       命名方式:basename_language_country.properties
       struts.xml  <constant name="struts.custom.i18n.resources" value="baseName"/>
国际化资源加载的顺序  : 离action 较近的将优先加载

2.如何在页面上和Action类中访问国际化资源文件的value值
    在Action类中,若action实现了TextProvider接口,则可以调用其getText()方法获取value值
        通过继承ActionSupport的方式实现 TextProvider接口
    
    在页面上可以使用s:text标签;对于表单标签可以使用表单标签的key属性值  
        key属性值直接在资源文件中找对应的值
        利用占位符,则可以使用s:text标签的s:param子标签来填充占位符(time=Time:{0})0就是占位符
    可以利用标签和ognl表达式直接访问值栈中的属性值(对象栈和Map栈)
        页面上的label属性使用%{getText('username')} 的方式也可以从国际化资源中获取到值
        是因为此时在对象栈中有DefaultTextProvider的一个实例,该对象中提供了访问国际化资源文件的getText()方法
        同时还需要通知Struts2框架 label中放入的已经不在是一个指定的字符串,而是一个OGNL表达式 使用 ${}包起来进行强制解析
   页面上如果是simple的主题 需要添加<s:text name="username"> 这样的标签 就能实现国际化
   有关占位符访问的
   资源文件中的内容  time=Time:{0}
   页面的代码  <s:text name="time">
           <s:param value="date"></s:param>
           </s:text>
    time=Time:${date}
    <s:text name="time"/>
通过超链接实现国际化
关键在于struts2框架是如何确定Locale对象的  可以通过I18N拦截器知道
流程 :>i18n拦截器在执行action方法之前,自动查找请求中一个名为request_locale的参数。
        如果参数存在拦截器就将其作为参数转换成Locale对象,并将其设为用户默认的Locale,
        并把其设为session的WW_TRANS_I18N_LOCALE属性
       >若request没有名为request_locale的参数,则i18n拦截器会从session中获取WW_TRANS_I18N_LOCALE的属性值
    若该值不为空,则将该属性值设为浏览器默认的Locale
       >若Session中的WW_TRANS_I18N_LOCALE属性值为空,则从ActionContext中获取Locale对象
   具体实现只要在超链接的后面加上request_locale=zh_CN(en_US)
   注意:超链接只能是Struts2的请求 i18n拦截器只能在struts下工作

Struts2运行流程分析
1.请求发送给StrutsPrepareAndExecuteFilter
2.StrutsPrepareAndExecuteFilter询问ActionMapper:该请求是否是一个Struts请求(即返回一个非空的ActionMapping对象)
3.若ActionMapper认为该请求是一个Struts2的请求,则StrutsPrepareAndExecuteFilter把请求处理交给ActionProxy
4.ActionProxy通过Configuration Manager 询问框架的配置文件,确定需要调用的Action类及action方法
5.ActionProxy创建一个ActionInvocation的实例,并进行初始化,
6.ActionInvocation实例在调用Action的过程前后涉及到相关拦截器的调用
7.Action执行完毕,ActionInvocation负责根据Struts.xml中找到对应的返回结果。调用结构的execute()方法,渲染结果
8.执行各个拦截器invocation.invoke()之后的代码
9.把结果发送到客户端


Struts2的输入验证 主要是服务端的验证
分为两种
    1、编程式验证
       Action类继承ActionSupport  
       重写validator方法进行这个类中的公共的校验
       如果要对指定的方法校验的话 方法名 必须是validateLogin()
       在这两个方法中 使用addFieldError(String key,String value) 来放错误消息
        在页面中可以通过 <s:debug>标签来查看错误消息
       注意:validator方法中一旦放入错误信息,程序就不会进入原申请的action方法中去 直接返回一个input的字符串(在struts的配置文件中配置一个全局的result name="input")
    2、声明式验证  
       对哪个action或者model的哪个字段进行验证
       使用声明验证规则
       如果验证失败,转向哪个页面,显示什么消息
声明式验证的一般流程
    1.先明确对哪个action的哪一个字段进行验证
    2.编写配置文件
      > 把struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example下的Login-validation.xml文件复制到当前的Action所在的包下
      > 把该配置文件名改为 :把Login改为当前Action的名字
      > 编写验证规则:参见E:\SSH\Struts\struts-2.3.15.3-all\struts-2.3.15.3\docs\WW\docs\validation.html 文档
      > 在配置文件中可以配置错误消息的
      <field name="age"> 要验证的字段
      <field-validator type="int"> 使用的验证规则 叫int
        <param name="min">20</param>
        <param name="max">50</param>
        <message>Age needs to be Between ${min} and ${max} </message>
      </field-validator>
      </field>

    3.若验证失败,则转向input 的那个result 所以需要配置name=input 的result
    4.如何显示错误消息呢 ?
    若使用的是非simple 主题 错误消息会自动显示
    若使用的是simple主题则需要s:fielderror标签 或 直接使用ognl表达式
    <s:fielderror fieldName="age"></s:fielderror>
注意 :若一个Action类可以应答多个action请求,多个action请求使用不同的验证规则 怎么办?
     为每一个不同的action请求定义其对应的验证文件 : ActionClassName-AliasName-validation.xml

     不带别名的配置文件ActionClassName-validation.xml 中的验证规则依然会发生作用。可以把各个action共有的验证规则配置其中,
        但只适用某一个action的请求的验证规则就不要在这里配置了    

Struts一共有15个内置的验证程序

设置短路验证:若当前验证没有通过,则不再进行下面的验证
<validator />元素和<field-validator /> 元素可以指定一个可选的short-circuit属性,该属性指定该验证器是否是短路验证器,默认为false
若类型转换失败,默认情况下还会执行后面的拦截器,还会进行验证,可以通过修改ConversionErrorInterceptor 源代码的方式使
   当类型转换失败时,不再进行后续的拦截器验证,而直接返回input 的result
   Object action = invocation.getaction();
   if(action instanceof ValidationAware){
    ValidationAware va = (ValidationAware)action;
    if(va.hasFieldErrors() || va.hasActionErrors()){
        return "input";
    }
   }

非字段验证 expression
<validators type="int">
    <validator type="expression">
    <param name="expression"><![CDATE[password==password2]]></param>
    <message>password must be equal</message>
    </validator>
</validators>
显示的时候用 <s:actionerror />


错误消息的重用性   多个字段使用同样的验证规则 能不能使用同一条验证消息
只需要<message>Age needs to be Between ${min} and ${max} </message>改为
<message>${getText(fieldName)} needs to be Between ${min} and ${max} </message>
在i18n文件中
<message>${getText(fieldName)} needs to be Between ${min} and ${max} </message>
age=年龄
count=数量

这样写 就可以显示中文的错误信息


自定义验证器
1.定义一个 验证器的类
  >需要实现一个接口Validator
   可以选择继承ValidatorSupport或FieldValidatorSupport
   若希望实现一个一般的验证器继承ValidatorSupport  若希望实现一个字段验证器,则继承FiledValidatorSupport

2.在配置文件中配置
  >默认情况下,Struts2会在类路径的根目录下加载validators.xml文件。在该文件中加载验证器

3.使用



Action传递json对象的时候
首先需要导入Struts2-json-plugin-2.3.8.jar
还有jackson=core-2.2.3.jar
struts.xml文件中的package需要继承json-default
 action中对应的result不需要写返回值
 result中的type属性需要设置为json
0 0
原创粉丝点击