早就听说了struts2了,当时并不知道struts2是基于webwork的,还以为是struts1.x的升级,再后来又听说了跟struts1.x完全不一样的。不过一直都给理由自己没去看,由于最近想自己搞一个小项目,打算用struts2不用struts1.x,所以就去了解struts2,看了网上的资料后,看来要以一种新框架的态度去学习了,不能将struts1.x上的工作流程模式往struts2里套了。好了,不多说了,先大概的了解一下struts2与struts1.x之间的差异吧
不同点 struts1 strust2 Action 类 要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现 常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。 线程模式 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题) Servlet 依赖 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。 可测性 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。 捕获输入 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经 常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存 在的JavaBean(仍然会导致有冗余的javabean)。 直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过 web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种 ModelDriven 特性简化了taglib对POJO输入对象的引用。 表达式语言 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。 可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL). 绑定值到页面(view) 使用标准JSP机制把对象绑定到页面中来访问 使用 "ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。 类型转换 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。 使用OGNL进行类型转换。提供基本和常用对象的转换器。 校验 支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。 支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性 Action执行的控制 支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。 支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。
(以上对比是转载过来的)
知道了它们之间的差异后,就开始动手做一个最最最基本的struts2页面转发的小程序,让自己大概知道一下基流程吧:
(1)先新建成一个web工程。然后在classpath里导入以下几个包:
commons-logging-1.0.4.jar freemarker-2.3.8.jar ognl-2.6.11.jar struts2-core-2.0.9.jar xwork-2.0.4.jar 其余的包通通删除掉,要不然会报一个严重: Exception starting filter struts2 Unable to load bean: type:com .opensymphony .xwork2 .ObjectFactory class:org.apache.struts2.spring.StrutsSpringObjectFactory 这样的错的,至于什么原因不太清楚,网上说的是什么依赖包的问题,反正只留上面那五个包就OK了。另,还有一点就是最好在tomcat5.5以上运行,我试过在5.0下运行,也是报错的。上网查了下资料说下一个5.5以上的就好了。tomcat6.0的下载地址:http://tomcat.apache.org/download-60.cgi 。选zip的下载。哦,还有struts2的下载地址:http://struts.apache.org/download.cgi#struts20111 。
(2)更改web.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>Struts2 Blank</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</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>这个更改的目的应该是将所有的请求都经过FilterDispatche过滤。
(3)在WebRoot目录下新建Login.jsp,WellCome.jsp两个JSP页面,然后再将Login.jsp修改如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>登录</title> </head> <body> <s:form action="login" namespace="/Login"> <tr><td>用户名:<input name="username"></td></tr> <tr><td>密码:<input name="password"></td></tr> <tr><td><input type="submit" value="提交"> </td></tr> </s:form> </body> </html>在这个jsp中,用到struts-tags这个tld,但你会并不会发现你的项目中有这一个文件存在,那到底在那里呢,原来是在struts2-code-xxx.jar包里的META-INF目录下。也用到了<s:form>这个struts2自带的检签,其实下面的那些用户名及密码也可以用检签的。只不过我不太习惯的原因,至于为什么要用到<s:form>这个,是因为有这个会比较方便一些,查看源代码你就会知道所生成的action路径是什么的了。而且<tr>标签上面也没有<table>,在看源代码的时候你会看到<s:form>这个标签已经生成一个<table class="wwFormTable">.
WellCome.jsp就随便写上一些东西吧!
(4)在src目录下分别建立struts.xml,login.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> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="false" /> <include file="login.xml"/> <!-- Add packages here --> </struts>在这里你会发现有一个<include file="xxx.xml">,是不是令你想你spring里的applictionContext.xml文件里也可以用一个类似的标签呢,无错,在spring中用的是<import file="xxxx.xml">,其作用与目的是一样的。都是为了方便维护。
login.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> <package name="com.struts2.test" namespace="/Login" extends="struts-default"> <action name="login" method="login" class="com.struts2.test.Login"> <result name="input">/Login.jsp</result> <result name="success">/WellCome.jsp</result> </action> </package> </struts>
在这个xml里,name是指定你的包路径,namespace就是指定你的命名空间。这个要与上面的Login.jsp的<s:form>里的namespace内容一致,因为如果不一样的话,就会有一个警告,http://hi.baidu.com/mum0532/blog/item/128f9a64fa8594f5f6365457.html ,这个网页里说得好清楚。(如果这里写了namespace="xxx",那返回的路径就是这样的:http://localhost/Login/login.action,如果这里不写,且页面也没写的话就会返回一个http://localhost/login.action的路径)
(5)在src目录下建一个BaseAction的类,内容如下:
import com.opensymphony.xwork2.ActionSupport; public class BaseAction extends ActionSupport { }暂为空的,只是extends了ActionSupport,让这个BaseAction变成我们的基类Action,以后在这个Action里新增一些公用的方法,如检查session,令牌等等。
(6)在src目录下新建一个包,包路径为你在login.xml文件里指定的那个。如com.struts2.test,在这个包下再新建一个类Login,Login的内容如下:
package com.struts2.test; public class Login extends BaseAction{ private String username; private String password; public String login() throws Exception { if (isInvalid(getUsername())) return INPUT; if (isInvalid(getPassword())) return INPUT; return SUCCESS; } private boolean isInvalid(String value) { System.out.println("value: "+ value); return (value == null || value.length() == 0); } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
由于在struts2中每个pojo都可以成为一个action,而取值也可以直接通过这个pojo取,isInvalid()方法里可以证明到这一点,在这个pojo里,如果用户名与密码有一个没有输入则返回一个字符串,这个字符串会与上面login.xml文件里的 <result name="">的name相配对,这个与struts1 action里的mapping.findForward("xxx")有点相同。
此时,布署在tomcat6.0上,然后运行,你会发现一个简单的页面转向完成了。对于struts2来说只是一小步,对于我来说是一大步了。