Struts2学习心得

struts2的学习心得,稍做一些记录 ,如下:

1. 首先,struts2实在WebWork2基础上发展而来的,同时也属于MVC框架。struts2 和struts1的一些区别,你可以自己去揣摩,我自己看了一些资料,总结几点如下:

     a. struts2不像struts1一样与servletAPI和strutsAPI之间存在紧密的耦合性,既不依赖于strutsAPI的Action,也不依赖于servletAPI的HttpServletRequest 和HttpServletResponse类,也可以说它不像struts1属于侵入式设计,而它属于非侵入的设计;

     回顾一下struts1的Action:

     public class TestAction extends Action{

        public ActionForward  execute(ActionMapping mapping, ActionForm form, HttpServletRequest,      HttpServletResponse response) throws Exception

        {

            if (...)

                return mapping.xxx;             

    }

     b.  struts2提供了拦截器,利用拦截器可以进行AOP(切面)编程,实现如权限拦截等功能;而要实现struts1的拦截功能,相对比较复杂;

     c.  struts2提供了类型转换器,可以将一些特殊的请求参数转换为所需的类型;而struts1中如果要实现类似功能,就必须向struts1底层实现BeanUtils注册类型转换器才行;

     d.  struts2除了提供JSP,还提供了如freeMarker ,Velocity等其他页面表现技术;

     e.  struts2提供了对指定方法的校验,解决了struts1的校验之痛,因为struts1中的校验是针对所有的方法,而不能单独提出对某些方法的校验;

     f.   提供了几种实用的国际化资源文件管理方式,如全局的资源文件,包范围和Action范围,这样就更加灵活;

 

2.  对于struts2的搭建,首先需要下载struts2的资源包,可以在apache的官网上面下载相关资料:http://struts.apache.org/download.cgi ; 然后对其进行解压缩;其中常用的jar包和一些支持第三方的jar包都可以在lib目录下面找到,同时apps包提供了一些例子,并且我们在配置环境时,所用到的web.xml和struts.xml配置文件都可以从这些app中拷贝过来;步骤有3,如下:

    a. 在MyEclipse里面新建一个web project,然后在项目属性中的java build path里的Libraries tab里面添加以下jar包作为Referenced Libraries:(我使用的是struts2.2.1)

        struts2-core-2.2.1.jar

        xwork-core-2.2.1.jar

        ognl-3.0.jar

        commons-logging-1.0.4.jar

        commons-fileupload-1.2.1.jar

        commons-io-1.3.2.jar   (有些童鞋会忘记这个包)

        freemarker-2.3.16.jar

        javassist-3.7.ga.jar  (这个包也很关键,据说是在2.1之后需要使用到,而且在lib包里面找不到,你需要在apps里面的struts2-blank-2.2.1.war里面的lib中copy出来)

    b. 上面是最基本的struts2所需jar包,接下来进行struts.xml和web.xml配置;可以拷贝apps里面项目的web.xml覆盖自建项目中WebRoot/WEB-INF的web.xml,然后去掉其他的一些配置,剩下内容如下:

        <?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<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>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

     这里想说两句的是,struts1框架是通过Servlet来启动的,而struts2环境是通过Filter来启动的,所以可以看到上面配置中对filter进行了配置,该类名StrutsPrepareAndExecuteFilter也可以看出是为启动struts2做准备;另外,这个filter类在2.1.13之前是采用另一个类,叫做org.apache.struts2.dispatcher.FilterDispatcher,现在它已经过时了;

     c. 配置上面的web.xml,其中的filter类的init()方法会在服务器容器启动项目的时候,调用类路径下面的struts.xml文件,然后把struts.xml中的内容以javaBean的形式加载到内存里面,所以这个struts.xml文件的读取是一次性的,而不是每次请求都让filter去读取;我觉得这一点在编程中很重要,因为使用了cache的方式,可以提高访问效率,但是值得注意的是,如果大量使用服务器内存cache,可能使服务器资源大量消耗,影响其运作效率;可以从apps的项目中copy struts.xml来编辑一下,且看struts.xml最基本配置:

     <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
</struts>

      完成以上3个步骤,就可以在服务器容器中启动所建项目了。     

 

3.  struts2中struts.xml的action配置:

     struts2中的action配置采用了package的形式,这样方便于被其他的package继承,但是它跟java class的package没有关系,也不能体现出层的概念,但是要求package的name属性必须唯一,可以将同一模块或者同一种用途的action放在一个package里面;每个package必须继承struts-default包,因为此包是struts2的核心包,struts2

的很多核心功能都是通过拦截器来实现的,而这个包定义了拦截器和Result类型; 其中的namespace属性是定义被访问的action的命名空间,此ns属性解决了struts1中大量重复定义相同action路径的问题,比如这些action, action name如下:/user/user-page/left,/user/user-page/right,/user/user-page/up,/user/user-page/down, 在struts2中可以如下定义:namespace为/user/user-page,而各个action命名为 letf, right, up, down,减少了重复配置的代码量;可以不指定ns,或者ns的名字指定为"",这样他就成了一个默认命名空间,如果一个请求在指定的ns中没有找到对应的Action,则会到这种默认的命名空间去寻找action;其中的method和result就是action的被访问方法名和返回的结果:

    <struts>
<package name="firstPackage" namespace="/hello" extends="struts-default">
<action name="helloWorldAction" class="com.bell.action.HelloWorldAction" method="returnMessage">
<result name="success">/WEB-INF/view/helloWorld.jsp</result>
</action>
</package>
</struts>

      HelloWorldAction类的定义很简单,因为struts2是非入侵的架构,所以aciton的method方法返回的类型也是String类型,其必须对应struts.xml中的result的name属性,而不像struts1中返回ActionForward对象而依赖上了servletAPI,其方法参数也不需要制定HttpServletRequest和HttpServletResponse来获取和返回数据到Jsp等表现层:

      package com.bell.action;

public class HelloWorldAction {
private String msg;

public String getMessage()
{
return msg;
}
public String returnMessage()
{
msg = "this is my first request to Action";
return "success";
}
}
    注意的是,访问地址需要加上被访问的action的namespace,而且struts2的默认后缀是action:

    http://localhost:8080/struts2/hello/helloWorldAction(.action)

 

4. 在struts1中的action定义中,可以通过不指定class和method属性来让请求不进行Action类的逻辑处理,而直接转向另一个页面,如下:

  <action path="/user/user-page/addEmployeeUI" forward="/WEB-INF/page/addEmployee.jsp"/>

   当然,struts2也提供了类似的直接跳转的方式,其中如果不指定action的class属性,则默认为xwork API中的ActionSupport类,如果不指定method,则默认为execute()方法,如果不指定result的name属性,则默认其name=”success“:

   <action name="addEmployeeUI">

      <result >/WEB-INF/page/addEmployee.jsp</result>

   </action>

 

5. struts2中常用的转发类型有4种,和struts1一样,有dispatcher(默认的方式,在/WebRoot/WEB-INF内部派发)和Redirect方式,另外还有redirectAction和plainText方式;值得注意的是,dispatcher是在WebRoot/WEB-INF内部派发,而Redirect方式是浏览器重定向,所以看不到WEB-INF内部的视图,所以要重定向的URL必须是WEB-INF以外的,包括互联网上的URL;而redirectAction需要定义相应的参数来把请求转发到另一个action上面;plainText方式用的不多,主要是显示视图的源码,而不执行视图的显示,也需要定义相应的参数,例子如下:

   a. redirect方式  

<struts>
<package name="helloWord" namespace="/helloWorld" extends="struts-default">
<action name="helloWorldAction">
  <result  type="redirect">/index.jsp</result> //可以不定以name属性,而直接跳转到WEB-INF外的视图
</action>
</package>
</struts>
   b. redirectAction方式

   <package name="helloWord" namespace="/helloWorld" extends="struts-default">
<action name="helloWorldAction">
<result name="success" type="redirect">/index.jsp</result>
</action>
<action name="helloAction">
  <result type="redirectAction">helloWorldAction</result> //定义了type属性为redirectAction之后,就可以直接跳转到对应的action
</action>
</package>

       其中,如果跳转的目标action在其他包的时候,需要在result中指定两个参数,即是actionName和nameSpace,如下,其实为什么需要指定actionName和nameSpace参数,这些都是定义在了struts-default包里面,这个默认的struts2的核心包定义了拦截器和相应的result type,也就是为什么所有的包需要extends struts-default包的原因;

     <package name="helloWord" namespace="/helloWorld" extends="struts-default">
<action name="helloWorldAction" class="com.bell.action.HelloWorldAction"
method="returnMessage">
<result name="success" type="redirect">/index.jsp?getMessage=${message}
</result>
</action>
</package>
<package name="hello" namespace="/hello" extends="struts-default">
<action name="helloAction">
<result type="redirectAction">
<param name="nameSpace">/helloWorld</param>
<param name="actionName">helloWorldAction</param>
</result>
</action>
</package>

     
   c. plainText方式

   <action name="helloWorldAction">
  <result name="success" type="plainText">/index.jsp</result>  //当请求到来时,直接显示index.jsp的源码
</action>

     如果显示源码的视图index.jsp页面有中文字符,则需要定义location和charSet属性来让tomcat以UTF-8的格式去读取这个以UTF-8为编码格式的jsp页面,不指定charSet的话,就会以默认的GBK编码格式去读取这个UTF-8的jsp页面,这样就会出现乱码:

     <action name="helloWorldAction">
  <result name="success" type="plainText">

        <param name="location">/index.jsp</param>

        <param name="charSet">UTF-8</param>

 </result>
</action>

6. action中的result视图可以在URL location后面加入类似于EL表达式的参数来使该视图获取action的属性:

    <action name="helloWorldAction" class="com.bell.action.HelloWorldAction" method="returnMessage">
  <result name="success" type="redirect">/index.jsp?message=${message}</result> // ${message}即获取了action中的getMessage()方法;而message=中的message则是request的key,在视图jsp中可以通过<%= request.getParameter("message")%>来获取其value,或者通过EL表达式${param.message}来获取;
</action>
    但是,如果从action返回的参数中有中文字符时,可能返回乱码,那么就要在服务器上的action进行编码和客户端视图上面的解码,例子如下:

        服务器端的Action,将默认的中文字符的ISO-8859-1进行utf-8编码:

    public String returnMessage() throws Exception {
  this.msg = URLEncoder.encode("这是一条消息","utf-8");  //使用URLEncoder类的encode方法对其进行utf-8编码
return "success";
 }

        客户端中将页面的pageCoding设置为UTF-8,这时可以通过JSP Expression来获取参数,并进行解码:

        <%=URLDecoder.decode(new String(request.getParameter("message").getBytes("ISO8859-1"), "utf-8"),"utf-8")%>  // 通过string类的getBytes("ISO8859-1")获取请求参数的中文编码字节字符串,然后进行utf-8编码转换,最后utf-8的解码即可输出中文字符;关于为什么需要utf-8编码和解码,我个人觉得是因为请求在通过服务器tomcat的时候进行了iso8859-1的编码,所以到达客户端视图时,需要获取iso8859-1的字节编码,然后转换成utf-8的格式,最后利用URLDecorder的decode()方法进行解码;

 

     另外,可以定义全局的global-results,让其能够被多个action共享;可以将global-results放在一个包里面,然后其他需要使用其result的action包继承它,这样就可以使用了:

     <package name="base" namespace="/base" extends="struts-default">
<global-results>
<result name="base" type="redirect">/index.jsp</result>
</global-results>
</package>


<package name="helloWord" namespace="/helloWorld" extends="base">
<action name="helloWorldAction" class="com.bell.action.HelloWorldAction"
method="returnMessage">
</action>
</package>

 

7.  可在struts2的配置文件中定义常量,如web请求的默认后缀名是.action,而可以通过struts2的常量设置将其后缀名位置为任意值:

    <struts>
  <constant name="struts.action.extension" value="do"></constant>
   <struts>

   以下所有的配置文件都可以添加常量,常量加载的顺序是:struts-default.xml, struts-plugin.xml, struts.xml, struts.properties, web.xml; 如果出现重复的常量名,后面加载的常量会覆盖之前的;其中,在struts.properties中定义如下,但是不建议这样定义,建议定义在struts.xml中:struts.action.extension=do

 

8.  struts2的常用常量:

     a. 指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freeMarker、Velocity的输出

         <constant name="struts.i18n.encoding" value="UTF-8">

 

     b. 指定请求后缀

         <constant name="struts.action.extension" value="do">

 

     c. 设置浏览器是否缓存静态页面内容,建议开发的时候设置为false

         <constant name="struts.serve.static.browserCache" value="false">

 

     d. 设置当struts2配置文件修改时,是否自动重新加载,建议开发的时候设置为true

         <constant name="struts.configuration.xml.reload" value="true">

     e. 设置开发模式下打印出更多的详细信息,设置为true

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

     f. 设置默认的视图主题

         <constant name="struts.ui.theme" value="simple">

     g. 设置与spring集成时,spring来负责action对象的创建

         <constant name="struts.objectFactory" value="spring">

     h. 设置struts2是否支持动态方法调用,默认为true,可设置为false

         <constant name="struts.enable.DynamicMethodInvocation" value="false">

     f. 设置上传文件的总大小,非单个上传文件大小

         <constant name="struts.multipart.maxSize" value="10701096">

 

9. struts2中的action跟struts1中的action是不太一样的,struts1中的Action是采用单例模式设计的,也就是非线程安全的,每个用户的每个请求都是通过在服务器启动时生成的单例action实例;而struts2中的action采用的是原型设计模式,每个用户的每一个请求都会让容器(或者是spring容器)产生一个action实例供请求调用,所以它是线程安全的,但是我想这样的执行效率肯定相对struts1较慢,而且占用更多的资源,也就是牺牲了资源来实现线程安全的效果;

 

10. struts2也提供了类似struts1中的DispatchAction方式来通过请求参数的不同派发到同一个action里不同方法来执行请求,并且返回result;

      回顾一下struts1中的DispatchAction类,DispatchAction是一个抽象的Action,它根据request 中的parameter参数来执行相应的方法。通个这个Action类可以将不同的Action集中到一个Action文件中来,还是看看具体的例子吧:

      jsp页面通过href中不同的参数来调用同一个action中的不同方法:

      <a href='<html:rewrite action="/which_pet_dispatchAction"/>?mymethod=dog'>Dog</a>
      <a href='<html:rewrite action="/which_pet_dispatchAction"/>?mymethod=bird'>Bird</a>

    Action:

    public class TestDispatchAction extends DispatchAction { 
     public ActionForward dog(ActionMapping mapping, ActionForm form,  
      HttpServletRequest request, HttpServletResponse response)  throws Exception {  
         request.setAttribute("pet", "dog");  
        return mapping.findForward("result");  
     }  

    public ActionForward bird(ActionMapping mapping, ActionForm form,  
           HttpServletRequest request, HttpServletResponse response)  throws Exception {  
            request.setAttribute("pet", "bird");  
            return mapping.findForward("result");  
      }  
    }

 

    struts1 xml配置

    <action-mappings>
         <action path="/which_pet_dispatchAction"  
         type="com.struts.TestDispatchAction"  
         scope="request"  
          parameter="mymethod">  
     <forward name="result" path="/WEB-INF/page/showpet.jsp"/>  
       </action>  
</action-mappings>

   目前我知道struts2提供了2种的方式,一种是通过url中加入 “感叹号+action方法名” 实现,不过这种方式已经不提倡使用,而配置文件不需要修改:http://localhost:8080/struts2/helloWorld!other.action   其中other是在struts.xml的action中没有配置的方法,一般配置的默认方法为execute;

   另一种方法就是使用通配符,这个就比较好理解了,首先需要在struts.xml中action的name属性加上通配符 “星号” ’*‘,然后在method属性中定义好请求参数中该通配符的位置,一般来说是 {1}, 之所以需要定义method的位置,是因为action的name属性里面可以使用多个通配符,那么method到底是哪一个通配符对应的方法名呢,就需要定义好位置;并且在class等属性中也可以使用通配符;看看例子吧:

      <action name="helloWorldAction*" class="com.bell.action.HelloWorldAction"
         method="{1}">   //method的方法名就是请求中"helloWorldAction_"后面紧接着的字符串
         <result name="success">/WEB-INF/view/helloWorld.jsp</result>
   </action>

   请求URL : http://localhost:8080/struts2/helloWorld/helloWorldActionother.action   //action类中定义了other()方法

 

11.   关于struts2中接受请求参数的问题,与struts1不同的是,struts2不再采用ActionForm(FormBean)的形式来接受页面的数据并且让action处理对应的formBean,而是页面请求的数据可以与action中的字段直接连接起来,但是要求action类中定义与请求参数同名的属性(setXXX方法中的XXX),通过反射技术调用与请求参数同名的属性的setter()方法,对action类的字段进行赋值,其实struts2的action类就是一个带有execute等方法的POJO类,这样提高了代码的复用性;

       还是先回顾一下struts1中的formBean和Action吧:

       //JSP表单

       <form action="dyna.do" method="post">
           <input type="text" name="name" /><br/>  
           <input type="text" name="age" /><br/>  
           <input type="submit" value="提交">  
           </form><br>  
      ${dynaForm.map.name }<br/>  
      ${dynaForm.map.age }


       // struts.xml的配置:

       <form-beans>

           <form-bean name="dynaForm" type="org.apache.struts.action.DynaActionForm">

               <form-property name="name" type="java.lang.String" />
               <form-property name="age" type="java.lang.Integer" />  
           </form-bean>  
       </form-beans>

 

       <action path="/dyna" type="com.cao.action.DynaFormAction" name="dynaForm"          scope="request" input="/error.jsp">  //input定义了出错时跳转的视图,name定义了ActionForm类名
           <forward name="success" path="/dynaform.jsp" />  
      </action>

 

      //action类:

       public ActionForward execute(ActionMapping mapping, ActionForm form,
             HttpServletRequest request, HttpServletResponse response)  throws Exception {  
             // TODO Auto-generated method stub    
             DynaActionForm daf = (DynaActionForm) form;              //转型为org.apache.struts.action.DynaActionForm 
             System.out.println(daf.get("name")); //可以通过get属性名获取属性值  
             System.out.println(daf.get("age"));  
             return mapping.findForward("success");  
}

      而在struts2中,定义如下的action类:

     public class HelloWorldAction

    {

         private  String name;

         private void setPerson(Person person) //setter方法中set后面的字符串要与请求中的参数同名

        {

            this.person=person;

         }

 

         public String execute()

        {

             System.out.println(person.name); //可以通过person的属性名获取属性值  
             System.out.println(person.age);

             return "success";
        }

     }

 

      class Person

      {

          private String name;

          private String age;

 

          //这里需要注意的是,如果没有给出Person的构造函数是可以的,如果给了出非默认的构造函数,则需要把默认的构造函数也定义出来,否则实例化person对象时会报错;

          public Person ()  //默认构造函数

          {

 

           }

 

          public Person (String name, String age)

          {

              this.name=name;

              this.age=age;

          }

        }

      //JSP页面

        <body>
           <form action="<%=request.getContextPath()%>/helloWorld/helloWorldActionreturnMessage"
               method="post">
              name:<input type="text" name="person.name"/> //可以直接引用复合对象的属性
              <br />
              age:   <input type="text" name="person.age" />
               <br />
              <input type="submit" value="提交" />
          </form>
        </body>

 

      //struts.xml配置:

     <package name="helloWord" namespace="/helloWorld" extends="struts-default">
            <action name="helloWorldAction*" class="com.bell.action.HelloWorldAction"
                 method="{1}">  //请注意上面jsp中的action和xml中action中的*号
             <result name="success">/WEB-INF/view/helloWorld.jsp
              </result>
      </action>

 

12. struts2中也很好地支持了类型转换,而且提供了针对特定action类和全局的转换设置;需要重写DefaultTypeConverter类,一共有3个同名的类位于不同的包里面,其中这两个包下面的这个类是可以正常使用的:com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter和ognl中的这个类;

      举个例子,在客户端请求中的时间参数,一般struts2可以处理这种格式:yyyy-mm-dd,但是如果客户端出现yyyymmdd格式的日期参数,则struts2在利用发射技术填充action中的对应字段时,只会把它看做String,这样的话就会报错,因为该字段对应的setter方法肯定是以Date作为参数类型,而不是String,所以需要用到类型转换器来定义;struts2中的类型转换器是双向的,比如action的属性被显示到struts标签上,struts标签需要使用到String,这样需要把Date转换成String,也就是struts的类型转换器可以从一种类型转换成另一个类型,并且能够相反地转换,且看以下定义的类型转换器:

      public class DateStringConverter extends DefaultTypeConverter

     {

          @override //需要重写父类的convertValue方法, context是ognl表达式,value是请求参数值,toType是需要转换的类型

          public Object convertValue(Map<String, Object> context, Object value, Class toType)

          {

                 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmdd");

                 try

                 {

                      if (toType == Date.class)  //字符向Date类型转换

                      {

                          String [] params = (String [])value; // 之所以这里的value是字符数组类型,是因为客户端来的请求参数可能是类似comBox等等这些同名的复合数据,所以要使用数组来存放;

                          return dateFormat.parse(params[0]);  //parse方法是转换String到Date

                      }

                       else if (toType == String.class) //Date类型向字符转换

                       {

                           return dateFormat.format((Date)value);  //format方法是转换Object(Date)到String

                       }

                   }

                    catch(Exception e)

                    {}

             }                         

     }

      这样的类定义好了之后,就需要做一下配置:

      a. 针对某个action类的局部类型转换,需要定义在该action的包下,并且命名格式为:actionName-conversion.properties, actionName就是对应的Action类名,不需要加上包名,里面的配置也是key-value格式,key对应action类中的需要转换的字段,value就是类型转换器类的类名,需要加上包名:

          birthday=com.bell.common.DateStringConverter

      b. 全局的类型转换器,转换器类是一样的,只需要把配置的属性文件重命名为:xwork-conversion.properties,并且放在项目的根路径下面,即src下面,里面的内容也是key-value形式,key为待转换的类型,value是转换器类名:

           java.util.Date=com.bell.common.DateStringConverter

      可能需要注意的是,如果定义了类型转换器,那么需要遵循转换器对应的格式,而之前默认识别的格式可能不能在被识别。

 

13.  struts1中的action类继承了servletAPI中的Action,比如说Action, DispatchAction, MappingDispatchAction等等,这样的话,就可以直接获取request,session,application等范围,接着对其进行获取参数或者植入参数等操作;但是struts2中的Action类是非侵入式的类,没有依赖于servletAPI,所以不能够直接获取这些范围,而strtus2中把request, session, application封装在一些类中,可以通过这些类获取实例或者实现他们的接口来获取实例,方式有3,如下所示:在jsp页面中,可以通过EL表达式获取各个scope的参数,如${requestScope.name}, {sessionScope.name},{applicationScope.name}:

      a. 如果只是想通过这些范围植入数据,然后回显到视图的话,就可以使用ActionContext类的static方法getInstance()来获取到上下文context,它是一个Map,可以调用put() 方法以key-value方式直接植入request范围的数据;context.getSession()获取到session范围的Map,同样以put()方法植入session范围的数据;context.getApplication()获取到servletContext(application)的Map,同样采用put()方法植入数据;但是通过ActionContext类是不能直接获取到request,session,application的实例,所以有局限性:

          private void setScopeValue() {
               ActionContext context = ActionContext.getContext();
               context.put("req", "request范围");
               context.getSession().put("ses", "session范围");
               context.getApplication().put("app", "application范围");
          }

       b. 接下来的方式就是使用ServletActionContext,这个类比第一个类多了一个Servlet的实现,所以可以通过ServletActionContext.getRequest()获取request实例,ServletActionContext.getRequest().getSession()获取session实例,ServletActionContext.getServletContext()获取application实例:

           private void setScopeValue() {
                HttpServletRequest request = ServletActionContext.getRequest();
                HttpSession session = request. ();
                ServletContext application = ServletActionContext.getServletContext();
           }

        c. 还有一种方式,就是实现特定接口,让struts2在运行时自动注入这些范围的实例;这样的话,action类需要实现ServletRequestAware,ServletResponseAware,ServletContextAware等接口,然后定义好HttpServletRequest, HttpServletResonse, HttpServletContext字段,通过setter方法自动注入:

          public class HelloWorldAction implements ServletRequestAware,
                                   ServletResponseAware, ServletContextAware {
                private HttpServletRequest request;
                private HttpServletResponse response;
                private ServletContext context;
                private Date birthday;

                public void setServletRequest(HttpServletRequest req) {
                      this.request = req;
                }

                 public void setServletResponse(HttpServletResponse res) {
                      this.response = res;
                 }

                 public void setServletContext(ServletContext context) {
                      this.context = context;
                }
           }

 

14.  struts2中引入jstl标签跟struts1是一样的,下载好jstl.jar和standard.jar包,导入到/WEB-INF/lib里面进行编译,然后在jsp页面上定义标签的使用:<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>,这样就可以使用类似命名空间<c:xxx>下面的jstl组件(标签)了:

      <c:forEach items="{persons}" var="person">
            <c:out value="${person.name}" /><br/>
      </c:forEach>

15.  struts2中提供了很好的上传文件机制,首先需要在jsp页面中顶一个form, 指定action属性后,把type设置为: multipart/form-data, 其中特别注意method属性要设置为post,不然无法以二进制格式上传到服务器,接着就是顶一个input,类型为FILE,name为任意文件名uploadfile;接下来需要注意的是,action中对应的获取uploadfile的字段名必须跟jsp中file input的uploadfile一致,即: private File uploadfile; 并且设置setter方法,如果需要返回到其他页面,就要设置getter方法;如果想要获取jsp中file input的uploadfile name,则action类中对应的属性也需要同样定义,即private String uploadfileFileName; 文件类型ContextType也是一样:private String uploadfileContextType;

       jsp:

      <form action="${pageContext.request.contextPath}/helloWorld/helloWorldAction.action"
enctype="multipart/form-data" method="post">
  文件:<input type="FILE" name="upload">
         <input type="submit" value="上传">
     </form>


      action类:

      private File upload;
      private String uploadFileName;

      ...//省略setter方法
     public String execute() {
           String realPath = ServletActionContext.getServletContext().getRealPath("/upload"); //通过ServletActionContext的getServletContext方法获得application,然后获取要存放路径/upload的实地址;
           File saveFile = new File(new File(realPath), uploadFileName);
           if (upload != null) {
               if (!saveFile.getParentFile().exists())
                   saveFile.getParentFile().mkdirs();
              try {
                  FileUtils.copyFile(upload, saveFile);
              } catch (IOException e) {}
           }
            ActionContext.getContext().put("successMsg", "上传成功!"); //设置request范围的返回参数到视图
           return "success";
        }

     如果上传的文件太大,可能无法上传,这样的话,可以在struts.xml中设置<constant name="struts.multipart.maxSize" value="10701096">,但是如果要上传类似视频这种超大文件的时候,这种web上传的方式实行不通的,或者说是不稳定的,一般都需要采用应用程序的socket技术让文件进行二进制上传。

     对于多文件上传,需要做的事情就是定义name属性相同的多个File input组件,然后在action类中把需要反射视图上的多文件的setter方法定义为List或者File数组,然后遍历list或者string []来保存文件:

       jsp:

       <form action="${pageContext.request.contextPath}/helloWorld/helloWorldAction.action"
             enctype="multipart/form-data" method="post">
             文件1:<input type="FILE" name="upload">
             文件2:<input type="FILE" name="upload">
             <input type="submit" value="上传">
       </form>


       action类:       

       private File [] upload;
      private String [] uploadFileName;

      ...//省略setter方法
     public String execute() {
             String realPath = ServletActionContext.getServletContext().getRealPath("/upload");
             File folder = new File(realPath);
             if (!folder.exists())
                  folder.mkdirs();
             if (upload != null) {
                 try {
                    for (int i = 0; i < upload.length; i++) {
                        File saveFile = new File(folder, uploadFileName[i]);
                        FileUtils.copyFile(upload[i], saveFile);
                    }
                 } catch (Exception e) {}
             }
             ActionContext.getContext().put("successMsg", "上传成功!");
             return "success";
        }


16. struts2提供了非常不错的拦截器机制,而且大多数的核心功能都是通过拦截器interceptor来实现的;拦截器是功能如其名,就是起到对用户请求拦截的作用,在其内部判断出是否接受用户请求并继续用户调用action相应的方法,并且返回结果,结果既是action的result,或者是global-result;这个自定义的Interceptor类需要实现com.opensymphony.xwork2.interceptor.Interceptor接口,然后重写其intercept方法,经过判断如果interceptor允许请求通过并且继续执行action的对应方法,则return intercept.invoke(); 否则return其他定义在action内的result字符串或者global-result:

      import com.opensymphony.xwork2.interceptor.Interceptor;

      public class UserInterceptor implements Interceptor {
            public void destroy() {}
            public void init() {}
            public String intercept(ActionInvocation invocation) throws Exception {
                if (ServletActionContext.getRequest().getAttribute("userName").equals("bell")) { //判断jsp上的userName是否是bell来执行返回操作
                     return invocation.invoke(); //invoke()方法返回的是String,这个String代表了继续执行请求的action及其方法,当然,在action的该方法中,肯定会返回一个result(视图)
                }
                return "fail"; //fail定义的是一个登陆错误返回页面,可以继续返回到登陆页面
            }
      }

     定义好了interceptor类之后,我们需要把它注册到action中,一般来说,我们自定义了interceptor,然后在struts.xml中配置如下:

     <interceptor name="userInterceptor" class="com.bell.interceptor.UserInterceptor" />

,然后在action中引入即可:

   <action name="helloWorldAction*" class="com.bell.action.HelloWorldAction" method="{1}">
     <interceptor-ref name="userInterceptor"></interceptor-ref>
     <result name="success">/WEB-INF/view/helloWorld.jsp
     </result>
     </action>

,但是这样做有很多弊端,就如我们之前所说的那样,struts2的很多核心功能是通过interceptor实现的,如果你在action中只引入了自定义的interceptor,那么就像java类的构造器一样,默认的struts2的拦截器功能将不再对该action有效,所以你需要在引入自定义的拦截器的时候,同时加入struts2的默认拦截器;通常我们的做法是,定义一个interceptor集合, 其中首先可以自定义自己的interceptor,再自定义一个interceptor的stack,然后把默认的interceptor  stack加入到此stack中,即default-stack,然后加入自己刚定义的interceptor引用进去,要注意先引入默认的stack,然后引入自己的interceptor,因为interceptor是顺序执行的,首先要确保默认的拦截器生效,然后再拦截自定义的,如下所示:

    <interceptors>
         <interceptor name="userInterceptor" class="com.bell.interceptor.UserInterceptor" />
         <interceptor-stack name="userStack">
               <interceptor-ref name="defaultStack"></interceptor-ref>
               <interceptor-ref name="userInterceptor"></interceptor-ref>
         </interceptor-stack>
    </interceptors>

   然后让action引用组合好的userstack:

   <action name="helloWorldAction*" class="com.bell.action.HelloWorldAction" method="{1}">
          <interceptor-ref name="userStack"></interceptor-ref>
          <result name="success">/WEB-INF/view/helloWorld.jsp
          </result>
   </action>
   完成上述步骤之后,当请求到达action的时候,反射机制执行完setter方法之后,不会直接进入请求的方法,而进入interceptor,让interceptor决定是否继续,或者执行另外的视图;

  另外,可以让整个package下面所有的action都引用一个默认的interceptor或者interceptor stack:

  <default-interceptor-ref name="userStack"/>,这样就可以了。但是如果在此package的某个action下面再定义一个其他自定义的interceptor,则会完全覆盖掉这个default interceptor,除非你在引入自定义的interceptor之前先引入default interceptor:

     <default-interceptor-ref name="userStack" />
    <action name="helloWorldAction*" class="com.bell.action.HelloWorldAction" method="{1}">
          <interceptor-ref name="userStack" />
          <interceptor-ref name="otherInterceptor" />
           <result name="success">/WEB-INF/view/helloWorld.jsp
           </result>
    </action>

17.  struts2也同样提供了validate机制,可以对action类所有的方法或者某一些方法进行校验,有两种形式:手动编写valudate方法和XML校验,先介绍下手动编写validate方法吧;首先action需要继承ActionSupport类,然后重写其validate()方法,判断输入的数据,如果检验失败,则将错误信息加入到addFieldError方法中,然后可以通过struts标签<s:fielderror>在页面上显示出来;其中,如果校验失败,会跳转到struts.xml中action的名为"input"的视图:

      action类:

      public class HelloWorldAction extends ActionSupport {
          private String userName;
          private String password;
          .....
          public void validate() {
                if (userName == null || "".equals(userName.trim())) {
                     this.addFieldError(userName, "用户名为空");
               }
               if (password.length() < 6) {
                     this.addFieldError(password, "密码必须超过6位");
               }
               if (!Pattern.compile("^1\\d$").matcher(password).matches()) {   //可以使用正则表达式格式校验
                      this.addFieldError(password, "数据格式不正确");
                }
           }

       }

 

        JSP:

        <%@ taglib uri="/struts-tags" prefix="s"%>  //引入struts标签

        ....

        <body>

             <s:fielderror/>

             ....

        </body>

 

        struts.xml:

        <package name="helloWord" namespace="/helloWorld" extends="base">
             <action name="helloWorldAction*" class="com.bell.action.HelloWorldAction" method="{1}">
                  <result name="success">/WEB-INF/view/helloWorld.jsp
                  </result>
                 <result name="input">/index.jsp</result>  //input视图为校验失败跳转视图
              </action>
         </package>
    另外,可以针对action的某一些方法进行,其他的没有太大区别,唯一的是validate方法名改为validate加上需要校验的方法名,并且此方法名首字母大写,如: validateExecute(),这样的话,只有execute方法会被校验;

    总结一下,输入校验的过程:

    a. 用户提交请求到struts2,struts2分配到对应action,action接受到请求参数,此时需要对请求参数进行类型转换,然后要利用反射技术对action类的字段进行注入值,如果在类型转换的过程中出现异常的话,系统会将异常情况保存到ActionContext中,conversionError拦截器将异常信息封装在fieldErrors里,但是不管是否出现异常都将进行步骤b;

    b. 系统利用反射技术调用action中的validateXxx方法(如果定义了特定方法的校验方法的话),Xxx为方法名,前面已经说过了;然后再调用validate()方法;如果出现校验失败,则同样会把错误信息封装到fieldErrors里面;

    c.  系统如果判断出fieldErrors(对方错误信息的集合)的长度大于0的话,就会自动跳转到struts.xml中action的input视图,如果fieldErrors中没有错误信息,则会执行action中的处理方法;

 

18. 以xml的方式对action的方法进行校验,首先action也必须继承ActionSupport类,并且提供校验文件,校验文件需要放在action同一目录下,名为ActionClassName-validation.xml的格式,其中ActionClassName为action类的简单类名,即不含包的类名,validation.xml是固定格式; 先看看一个例子:

      //HelloWorldAction-validation.xml,如果采用的DTD是xwork-validator-1.0.3.dtd的话就如下配置

      <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator Config 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<validators>
 <field name="userName">   //对应Action类中的属性
  <filed-validator type="requiredstring">  //requiredstring是struts2定义好了的验证类型
  <param name="trim">true</param>  //默认为true,让userName进行trim()去掉两头的空格,然后再判断
  <message>用户名不能为空</message>  //输出的错误信息
</filed-validator>
</field>
</validators>


     顺便说说DTD文件,document type definition,DTD是一种保证XML文档格式正确的有效方法,可以比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。其实,大家可以看看xwork-core-2.x.x.jar里面的xml,dtd文件,这样就更加了解这些xml的配置和struts2提供的一些操作方式和规则;

     采用如上的xml文件,然后和action放在同一包里面,就可以生效了,同上面17点所说的,如果验证不通过则进入action的input视图,否则继续执行action的请求方法。其中field-validator的type类型有很多种,常用的有:required,requiredstring,stringlength,regex(正则表达式)等等,大家在需要的时候可以去google一下他们的用法,大同小异,但是reqex要稍微了解一下,它提供了非常用验证的很多验证方式。

     要通过xml形式对action的指定方法进行校验,就需要把xml定义为ActionClassName-ActionName-validation.xml,其中,ActionName就是struts.xml中配置的Action Name,也即是请求视图中action名,比如jsp中form的action是:${pageContext.request.contextPath}/helloWorld/helloWorldActionlogin.action,则这里的ActionName就是helloWorldActionlogin:HelloWorldAction-helloWorldActionlogin-validation.xml;其他的配置和所有方法验证的xml配置一样。

    如果一个action既有全局的验证xml,也有对某些特定方法的xml验证,那么struts2会先搜索全局的xml校验,然后综合所有的xml验证进行验证,如果存在冲突,则采用局部的特定方法的xml验证;如果一个action继承了另一个action,则会先搜索父类action的验证文件,然后搜索子类的,同样的,全不会起作用。

 

19. struts2也提供了国际化信息输出的方法,首先准备好资源文件,放在src目录下,命令格式有如下几种:baseName_language_country.properties, baseName_country.properties, baseName.properties;其中baseName可以随便定义,但是language和country要是java所支持的语言和国家,可以从IE的语言选项里面看到他们的简写编码;中国大陆就是baseName_zh_CN.properties, 美国baseName_en_US.properties. 此属性文件里面的内容以key-value的形式存在,如:name=李小龙,name=Bruce Lee;但是对于中文的属性文件,我们要用jdk提供的native2acsii命令把文件转换为unicode编码的文件,命令的格式是: native2ascii 源文件 目标文件;但是幸好的是,从MyEclipse 6.6 (可能)之后,就提供了自动转换的功能,不用native2ascii命令手动去转换到unicode编码; 准备好了资源文件之后,我们就在struts.xml中通过struts.custom.i18n.resources常量把资源文件定义为全局资源文件:<constant name="struts.custom.i18n.resources" value="baseName"/>;然后呢,在JSP页面我们可以通过struts标签<s:text name="key"/>来输出国际化信息;在Action类中,可以让它继承ActionSupport,使用getText()方法来得到国际化信息,此方法有很多重写,但是第一个参数适用于指定资源文件中的key;表单标签中也是通过key指定资源文件的key,如:<s:textfield name="realname" key="user"/>;

      对于使用了占位符的国际化信息,如:info={0},this is a {2};在jsp中还是使用struts的标签<s:text>中的<s:param>依次填写{0},{1}对应的value;action类中的话,通过getText()的getText(String key, String[] args)或者getText(String aTextName, List args)方法填充值;

      如果只采用全局的资源文件,那样是不容易维护和查找的,所以我们可以采用包范围国际化资源文件,它能够被该包及其子包使用;格式就是package_language_country.properties,其中package是固定写法,其他的跟全局一样,然后要把这种包范围的国际化资源文件放到对应包下面。而且包范围的优先级高于全局的,对应的action或者WebRoot中的jsp会先按照key寻找包范围的文件,如果找不到,就会寻找其父类包的资源文件,最后是全局范围的文件;

      同样,对于某个具体的Action,struts2也提供了action范围的国际化资源文件,同上,以ActionClassName_language_country.properties为命名的格式,然后放在action的类路径就可以了,同样,如果action类通过key来查找key的话,如果action类没有对应的key,就会在package范围的资源文件查找,然后是父包,然后是全局;

      如果我们不想配置采用具体的资源文件,可以使用struts中的<s:i18n>标签来指定使用action或者package的资源文件,例如使用HelloWorldAction范围的资源文件,name属性就在包后面加上ActionClassName

           <s:i18n name="com/bell/action/HelloWorldAction">

                 <s:text name="welcome"/>

           </s:i18n>

      如果采用某个包下面的资源文件,name属性中在包的后面加上package这个固定字符。

           <s:i18n name="com/bell/action/package">

                 <s:text name="welcome"/>

           </s:i18n>

 

20.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值