`
feicer
  • 浏览: 133169 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

struts2 学习笔记

阅读更多
struts2 学习笔记
J2EE里的框架已经很多了,既然struts1的用户群庞大,为什么还要继续开发出struts2呢?虽然两者看上去似乎就只有版本的区别,但是实际上它们的本质已经有了很大的区别。Struts2是在WebWork2基础发展而来的,它属于MVC框架。因为本质上有很大差别,所以Struts2 和struts1在代码编写风格上几乎是不一样的。需要推出struts2的主要是原因有以下一些,同时它们也是struts2有优点(不然也没有存在的价值):
      1.在软件设计上Struts2没有像struts1那样跟Servlet API和struts API有着紧密的耦合,Struts2的应用可以不依赖于Servlet API和struts API。Struts2的这种设计属于无侵入式设计,而Struts1却属于侵入式设计。所谓侵入式,看如下以前的增加学生的Action方法签名:
      public class AddStudentAction extends Action { 
            public ActionForward execute(ActionMapping mapping, ActionForm form, 
                        HttpServletRequest request, HttpServletResponse response) 
                        throws Exception { 
            } 
      } 

    从上可以发现,方法中需要传入request和response对象,这就使Action类依赖了Servlet API,同时前面两个参数是struts1自身的类型,所以该类同样依赖了struts1的API。对比struts1这段代码,在以后将会看到 struts2是完全的POJO类,这样复用性也随之提高。
      2.Struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能。
      3.Strut2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型。在Struts1中,如果要实现同样的功能,就必须向Struts1的底层实现BeanUtil注册类型转换器才行。
      4.Struts2提供支持多种表现层技术,如:JSP、freeMarker、Velocity等。
      5.Struts2的输入校验可以对指定方法进行校验,解决了Struts1长久之痛。
      6.提供了全局范围、包范围和Action范围的国际化资源文件管理实现。
    因此,可以预见随着时间当天推移,strut2将会逐渐替代struts1成为开发人员的首选框架。先来实际认识struts2,第一步当然是搭建开发环境了,一般都是三步:导入开发Struts2应用需要使用到的jar文件、编写Struts2的配置文件和在web.xml中加入Struts2 MVC框架启动配置。struts2的jar包以及文档可以去官网下载,目前最新版为2.1.8。下载完后解压文件,开发struts2应用需要依赖的 jar文件在解压目录的lib文件夹下。不同的应用需要的JAR包是不同的,下面是开发Struts 2程序最少需要的JAR:
      struts2-core-2.x.x.jar :Struts 2框架的核心类库
      xwork-core-2.x.x.jar :XWork类库,Struts 2在其上构建
      ognl-2.6.x.jar :对象图导航语言(Object Graph Navigation Language),struts2框架通过其读写对象的属性
      freemarker-2.3.x.jar :Struts 2的UI标签的模板使用FreeMarker编写
      commons-logging-1.x.x.jar :ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。
      commons-fileupload-1.2.1.jar 文件上传组件,2.1.6版本后必须加入此文件
    以上就是struts2依赖的最小jar包,特别是最后一个上传组件的jar包,以前版本不是必须的。如果用2.1.6以后的就需要加入lib目录中。
    导入jar包以后,接下来要做的编写struts2的配置文件,该文件和struts1的空文件类似,只是其中的元素换成了<package>,配置文件将在第一个struts2的应用示例中进行详细说明。在这里可以把它看成是和web.xml一样的文件。搭建环境也只需要复制一份空的struts.xml配置文件,这可以从文档或者示例中进行复制,保留好DOCTYPE的声明和根元素<struts>就可以了。
    第三步就是将struts2集成到web应用中,换句话就是说让web程序启动时,能将struts2初始化。在struts1.x中, struts框架是通过Servlet启动的。而在struts2中,struts框架是通过Filter启动的。具体在web.xml中的配置如下:
      <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>

    以上配置表明,在servlet容器启动的时候,过滤器将被初始化,在初始化过程中,StrutsPrepareAndExecuteFilter的 init()方法中将会读取类路径下默认的配置文件struts.xml完成初始化操作。所以默认情况下struts.xml应该放在类路径下,并保持默认的取值。有一点需要说明的就是,在Struts 2.1.3以前,<filter-class>是用的FilterDispatcher类,但现在已经被标注为过时的。所以2.1.3以后的版本就应该用StrutsPrepareAndExecuteFilter类。和struts1一样,struts2读取到struts.xml的内容后,它会以javabean形式存放在内存中,以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件。

    创建struts2的应用,首先应如前面所示要搭建好环境。jar包的导入和web.xml配置这里不在写出来。现在就来看第一个应用的 struts.xml这个配置文件:
      <struts> 
            <package name="hello" namespace="/test" extends="struts-default"> 
                  <action name="sayHello" class="com.framework.struts.HelloWorld" method="execute"> 
                        <result name="success">/WEB-INF/page/index.jsp</result> 
                  </action> 
            </package> 
      </struts> 

    如上所示,struts2中是采用<package>元素来管理Action的,包的作用和java中的类包是非常类似的,它主要用于管理一组业务功能相关的action。在实际应用中,我们应该把一组业务功能相关的Action放在同一个包下。配置包时必须指定name属性,该name属性值可以任意取名,但必须唯一,它不对应java的类包,如果其它包要继承该包,必须通过该属性进行引用。包的namespace属性用于定义该包的命名空间,命名空间作为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径为:/test/sayHello.action。 namespace属性可以不配置,如果不指定该属性,默认的命名空间为空字符串。配置namespace有什么好处呢?就名称空间而言可以有效防止名字重复,即请求路径一样,这样就会导致错误。第二个就是当有名称空间时,可以减少配置的书写量,因为相同路径的action请求可以放置在同一个名称空间下,这样就不用每次都写出名称空间值所代表的路径。除了上述两个属性,通常每个包都应该继承struts-default包, 因为Struts2很多核心的功能都是拦截器来实现。如:从请求中把请求参数封装到action、文件上传和数据验证等等都是通过拦截器实现的。 struts-default定义了这些拦截器和Result类型。因此,当包继承了struts-default才能使用struts2提供的核心功能。struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定义。 struts-default.xml也是Struts2默认配置文件。 Struts2每次都会自动加载 struts-default.xml文件。细心点其实可以发现struts-default.xml的name为struts-default包有一个属性abstract="true",这表示该包为抽象包,抽象包中不能包含action,抽象包是专门用来被其它包来继承的。
    接下来就是<action>元素的配置,其中name属性为访问时路径的一部分,class为类的全限定名,最后就是一个method属性,表示应该被调用的方法。里面的<result>元素和struts1的forward元素一样,相当于视图的配置。接下来就是Action和 JSP的代码片断:
      public class HelloWorld { 
          private String message; 

          public String getMessage() { 
              return message; 
          } 

          public String execute(){ 
              message ="Hello World! 北京时间:"; 
              return "success"; 
          } 
      } 

JSP页面的<body>里面的内容:
      ${message}<%=new java.util.Date() %>
    写完以后,部署成功后,在浏览器中输入相应的URL(如.../test/sayHello.action)就会显示出消息和时间。其中execute方法要求必须是返回一个字符串,因为struts2根据这个返回的字符串来决定调用哪个视图。从编码过程来看,Action类是完全的POJO类,没有依赖任何类或者接口。
    在struts2中,Action的名称的搜索时遵循一定原则的,下面就是struts2在请求到来后,怎么去找到合适的action进行调用,总结如下 (获得请求路径的URI,例如url是:http://server/struts2/path1/path2/path3/test.action):
      1.首先寻找namespace为/path1/path2/path3的package,如果不存在这个package则执行步骤2;如果存在这个 package,则在这个package中寻找名字为test的action,当在该package下寻找不到action 时就会直接跑到默认namaspace的package里面去寻找action(默认的命名空间为空字符串""),如果在默认namaspace的 package里面还寻找不到该action,页面提示找不到action。
      2.寻找namespace为/path1/path2的package,如果不存在这个package,则转至步骤3;如果存在这个package,则在这个package中寻找名字为test的action,当在该package中寻找不到action 时就会直接跑到默认namaspace的package里面去找名字为test的action ,在默认namaspace的package里面还寻找不到该action,页面提示找不到action。
      3.寻找namespace为/path1的package,如果不存在这个package则执行步骤4;如果存在这个package,则在这个 package中寻找名字为test的action,当在该package中寻找不到action 时就会直接跑到默认namaspace的package里面去找名字为test的action ,在默认namaspace的package里面还寻找不到该action,页面提示找不到action。
      4.寻找namespace为/的package,如果存在这个package,则在这个package中寻找名字为test的action,当在 package中寻找不到action或者不存在这个package时,都会去默认namaspace的package里面寻找action,如果还是找不到,页面提示找不到action。
    总的来说,就是相当于是最大匹配原则,如果一直没有匹配的,则一级一级往上找,直至根目录。如果在某处出现了匹配路径,则去那个名称空间下找 action,这个时候如果找不到,就直接跳到默认package中去找了。如果仍然找不到,那就只能报错了。所以说,这个寻找过程是先匹配大的名称空间,有匹配了才去找相应的action,如果在匹配的名称空间没有需要的action,则直接跳到默认包中找,不会再次跳回去匹配名称空间了。

      在前面<action>元素都配置了相应的属性,struts2给某些属性提供了默认值。如果没有为action指定class,默认是 ActionSupport;如果没有为action指定method,默认执行action中的execute() 方法;如果没有指定result的name属性,默认值为success。因此,有了这些默认值,当在前面的应用中,这些属性都不配置,然后更改一下 JSP页面的内容,把输出message的EL表达式删掉。再次运行程序,发现仍然能显示出修改后的页面。查看ActionSupport的源代码,可以发现其中的execute方法返回的就是"success"字符串。
    以上是关于action元素的配置,result元素配置类似于struts1中的forward,但struts2中提供了多种结果类型,常用的类型有:dispatcher(默认值)、redirect、redirectAction和plainText四种类型。其中默认值dispatcher就是前面用到的,这种方式是服务器内部的转发。struts2提供了一个比较有用的功能:在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应action中的属性。如下:<result type="redirect">/view.jsp?id=${id}</result>,这种方式的重定向式浏览器重定向,相当于重新发一个新的请求,并且在重定向时,EL表达式中id的值能够从action获取到。另外一个重定向是redirectAction,如果重定向的 action在同一个包下,就就可以直接写上从定向目的地的action的名字。如果是不同的包下,就需要通过两个参数来指定action的名字和名称空间的值。同一个包下:
      <result type="redirectAction">helloworld</result>
如果重定向的action在别的命名空间下:
      <result type="redirectAction"> 
          <param name="actionName">helloworld</param> 
          <param name="namespace">/test</param> 
      </result> 

    最后一种类型就是plaintext,它用来显示原始文件内容,例如:当我们需要原样显示jsp文件源代码 的时候,我们可以使用此类型,需要注意的是一定要指定源文件的编码方式,否则显示的页面将出现乱码:
      <result name="source" type="plainText "> 
          <param name="location">/hello.jsp</param> 
          <param name="charSet">UTF-8</param> 
      </result> 

    当多个action中都使用到了相同视图,这时就应该把result定义为全局视图。全局视图的定义方式是将试图定义放在<global- results>中就可以了,但是只要只能是同一个包下action才能跳转到全局视图。如果要不同的包也能公用全局视图,可以专门为这些公用的全局试图定义一个包,注意这个包要继承struts-default,然后其需要用到这个全局视图的包再继承这个公共的包。action元素中除了定义视图元素外,还可以用来往Action类中的属性注入相应的值。所用的元素就是<param>,该元素的name属性需要和相应Action类的属性一致,并且要提供setter方法。
    前面都是默认使用.action后缀访问Action。其实在struts2中,默认后缀是可以通过常量struts.action.extension 进行修改的,例如:我们可以配置Struts 2只处理以.do为后缀的请求路径:<constant name="struts.action.extension" value="do"/>。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号隔开。除了这个常量外,还有其它一些常用的常量,总结如下:
      1.指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker、 velocity的输出:<constant name="struts.i18n.encoding" value="UTF-8"/>
      2.该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号隔开:<constant name="struts.action.extension" value="do,action"/>
      3.设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭:<constant name="struts.serve.static.browserCache" value="false"/>
      4.当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开:<constant name="struts.configuration.xml.reload" value="true"/>
      5.开发模式下使用,这样可以打印出更详细的错误信息:<constant name="struts.devMode" value="true" />
      6.默认的视图主题:<constant name="struts.ui.theme" value="simple" />
      7.与spring集成时,指定由spring负责action对象的创建:<constant name="struts.objectFactory" value="spring" />
      8.该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false:<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
      9.上传文件的大小限制:<constant name="struts.multipart.maxSize" value=“10701096"/>
    因为常量可以在下面多个配置文件中进行定义,如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值,struts2加载常量的搜索顺序:struts-default.xml、struts-plugin.xml struts.xml、struts.properties、web.xml。

      这些常量的配置是非常有用的,比如第5个常量,当配置成true以后,在开发中能大大的提高效率。处理错误时间也变短。当然了在开发中需要配置,但是在产品发布的时候这个配置就要取消了。第二个常量的配置可以按开发人员的习惯来命名后缀。如果用到文件上传操作,第9个配置就会起作用了,而且在程序中可以动态改变,减少了开发人员的工作量。

     与struts1流程相比,struts2的流程相对来说比较简单。StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,它负责拦截由<url-pattern>/*</url-pattern>指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求。默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入Struts 2框架处理,否则Struts 2框架将略过该请求的处理。当请求转入Struts 2框架处理时会先经过一系列的拦截器,然后再到Action。Action执行完后,就转入相应的视图并返回响应给客户端。与Struts1不同,Struts2对用户的每一次请求都会创建一个Action,所以Struts2中的Action是线程安全的。
    在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿。为了避免 struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。下面的struts.xml通过<include>元素指定多个配置文件:
      <struts> 
          <include file="struts-user.xml"/> 
          <include file="struts-order.xml"/> 
      </struts> 

    在struts1中,一个Action如果继承了DispatchAction,就能派发请求给不同的方法进行处理。在struts2中有两种方式实现类似功能。一是当Action中存在多个方法时,我们可以直接使用!+方法名调用指定方法。假设一个Action中有execute和other两个方法,正常情况下访问execute方式的URL路径为:/struts/test/helloworld.action,如果要访问action的 other() 方法,就可以这样调用:/struts/test/helloworld!other.action。动态调用很少被使用,一般都是采取后面介绍的那种方式来派发请求。如果不想使用动态方法调用,我们可以通过常struts.enable.DynamicMethodInvocation关闭动态方法调用。使用通配符定义action也比较简单,首先是在action元素的name属性处采用一个*号通配符,然后在method属性处用一个占位符。如下所示:
      <action name="helloworld_*" class="com.framework.struts.HelloWorld" method="{1}">
    如果要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action。
    如果使用的是2.1.8版本之前的struts2,则在程序中需要解决乱码问题。struts2.1.6版本中存在一个Bug,即接收到的中文请求参数为乱码(以post方式提交),原因是struts2.1.6在获取并使用了请求参数后才调用HttpServletRequest的 setCharacterEncoding()方法进行编码设置 ,导致应用使用的就是乱码请求参数。这个bug在struts2.1.8中已经被解决,如果你使用的是struts2.1.6,要解决这个问题,你可以这样做:新建一个Filter,把这个Filter放置在Struts2的Filter之前,然后在doFilter()方法里添加以下代码:
      HttpServletRequest req = (HttpServletRequest) request;
      req.setCharacterEncoding("UTF-8");//应根据你使用的编码替换UTF-8
      filterchain.doFilter(request, response);
    在struts2中,默认的表达式语言是OGNL,Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。相对EL表达式,它提供了平时我们需要的一些功能,如:支持对象方法调用,支持类静态方法调用和值访问,操作集合对象。例如():
      访问值栈中的action的普通属性: username = <s:property value="username"/>
      访问值栈中对象的普通属性(get set方法):<s:property value="user.age"/> | <s:property value="user['age']"/>
      访问静态方法:<s:property value="@com.framework.struts2.ognl.Test@s()"/>
      访问静态属性:<s:property value="@com.framework.struts2.ognl.Test@S"/>
      访问Math类的静态方法:<s:property value="@@max(2,3)" />
      访问普通类的构造方法:<s:property value="new com.framework.struts2.ognl.User()"/>
      访问List:<s:property value="users"/>
      访问List中某个元素:<s:property value="users[1]"/>
      访问List中元素某个属性的集合:<s:property value="users.{age}"/>
      访问List中元素某个属性的集合中的特定值:<s:property value="users.{age}[0]"/> | <s:property value="users[0].age"/>
    看看上面的实例代码如此方便的就访问到相应的属性。那它的原理是什么呢?Ognl 有一个上下文Context概念,其实这个上下文就是一个MAP结构,它实现了java.utils.Map接口,在Struts2中上下文 Context的实现为ActionContext,当Struts2接受一个请求时,会迅速创建 ActionContext,ValueStack,action 。然后把action对象存放进ValueStack,所以action的实例变量可以被OGNL访问。
    在放入action对象到值栈中的同时,与ValueStack平行的还有代表request,session,application的Map结构,当在程序中往这些Map中放入对象时,也就相当于与放入了相应的Servlet域对象中。在struts2中,OGNL会设定一个根对象,该根对象就是 ValueStack(值栈)。如果要访问根对象中对象的属性,可以直接访问该对象的属性,就如同上面看到的那样。在struts2中,根对象 ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。在OgnlValueStack类里有一个List类型的root变量,就是使用他存放一组对象。在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。
    由此看来struts2中存储对象的Map比较多,不肯能跟直接去访问它们。所以当药访问相应范围内的属性时就需要使用#符号标注命名空间,如#application、#session等,这样struts2就会去相应的范围类去寻找。有一点值得注意的是OGNL表达式需要配合Struts 标签才可以使用。
分享到:
评论

相关推荐

    Struts2学习笔记

    Struts2学习笔记,介绍了struts2的基础部分

    struts2学习笔记(完美总结)——转自OPEN经验库

    struts2学习笔记,非本人所写,但有学习的价值,总结的很好,分享一个!

    struts2学习笔记总结

    struts2学习笔记总结

    struts2学习笔记

    struts2学习笔记struts2学习笔记struts2学习笔记

    struts2学习笔记3数据类型转换

    struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换struts2学习笔记3数据类型转换

    struts2学习笔记.doc

    本人学习struts2的笔记,希望大家可以多多学习以后共同交流

    struts2学习笔记(1)

    1. struts2框架的引入 1)把struts2的相关jar包导入到项目中去 2)把struts2框架的配置文件struts.xml复制粘贴到项目中的src下面(同时也可以把log4j.properties放到src下) 在这里我们主要是要的这个struts.xml文件...

    struts2学习笔记(详细文字)

    structs2很详细的学习笔记,structs2的建造,工作原理,例子,逐步讲解,纯文字的

    Struts2 学习笔记

    01 Struts2-Action 5 一、 Struts作用: 5 二、 搭建Struts2的运行环境: 5 三、 Namespace 6 四、 标签 6 五、 Action 6 六、 路径问题的说明 8 七、 Action的动态调用方法 8 八、 Action通配符(wildcard)的配置 9 ...

Global site tag (gtag.js) - Google Analytics