【IT168 技术文档】
Tomcat
首先要安装好Tomcat,如果在安装的时候遇到任何问题,请查看Tomcat的安装指南
MySql
安装并配置MySQL。创建一个名为“quickstart”的数据库,并运行下面脚本来创建“Person”表。后面在applicationContext.xml里,我们将使用"root"数据库用户名和密码,记得用你自己的数据库设置来替换它们。
CREATE TABLE 'quickstart'.'Person' ( 'id' INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 'firstName' VARCHAR(45) NOT NULL, 'lastName' VARCHAR(45) NOT NULL, PRIMARY KEY('id'))ENGINE = InnoDB;
注:上面的DDL需要保存在文件中,然后在MySQL中导入。我直接复制然后在查询分析器中执行失败
创建Eclipse项目
- 打开Eclipse,我是认真的,你必须打开Eclipse
- 点击File -> New -> Project. 选择"Dynamic Web Project"并点击下一步(注:如果使用MyEclipse,这里不太一样)
- 输入项目名,这里我使用"quickstart"。这个项目将要在Tomcat中运行,所以我们需要为它创建应用服务器配置
- 在"Target Runtime"下面点击"New",选择"Apache Tomcat5.5"并点击下一步
- 输入Tomcat的安装路径并选择一下已安装的JRE(需要1.5)
- 现在你应该回到了项目创建向导,并且Tomcat是你的Target Runtime。点击下一步,选择"Dynamic Web Module"和"Java"facets,最后点"finish"。
(上面讲的都是Eclipse WTP中的配置,如果使用MyEclipse请自行修正)
库依赖关系
你的项目应该包含"src","build"和"WebContent"目录。我们把所有必需的jar文件放在"/WebContent/WEB-INF/lib"目录下。请复制它们到${workspace}\quickstart\WebContent\WEB-INF\lib目录。jar文件名的版本号已经被去除了!
| Jar | From |
|---|---|
| xwork.jar | Struts 2 |
| struts2-api.jar | Struts 2 |
| struts2-core.jar | Struts 2 |
| struts2-Spring-plugin.jar | Struts 2 |
| ognl.jar | Struts 2 |
| freemarker-2.3.4.jar | Struts 2 |
| mysql-connector-java.jar | MySql JDBC Driver |
| spring.jar | Sping 2.0 |
| antlr.jar | Hibernate Core |
| asm.jar | Hibernate Core |
| asm-attrs.jar | Hibernate Core |
| cglib.jar | Hibernate Core |
| dom4j.jar | Hibernate Core |
| jdbc2_0-stdext.jar | Hibernate Core |
| ehcache.jar | Hibernate Core |
| hibernate3.jar | Hibernate Core |
| xml-apis.jar | Hibernate Core |
| commons-collections.jar | Hibernate Core |
| ejb3-persistence.jar | Hibernate Annotations |
| jta.jar | Hibernate Annotations |
| hibernate-annotations.jar | Hibernate Annotations |
| hibernate-entitymanager.jar | Hibernate Entity Manager |
| javassist.jar | Hibernate Entity Manager |
| jboss-archive-browsing.jar | Hibernate Entity Manager |
右击项目点“刷新”,通知Eclipse我们加入了很多的jar文件。
我使用Struts2.0.6, Spring2.0.3, Hibernate3.2。struts2-api.jar找不到,没有也可以运行成功;Hibernate Annotations和Hibernate Entity Manager需要在Hibernate的主页上下载,不包括在Core里面;另外jta.jar和javassist.jar在Hibernate Tools里面,同样要下载;最后,上面列表并缺少一个包,因为Hibernate3.2对此有一点小小的修改,你需要把Hibernate Annotations里面的hibernate-commons-annotations.jar拷贝进来。
领域模型
我们的领域模型只有一个简单的"Person"类,它包含少量的实例变量。
- 创建一个新类并命名为"Person",然后输入"quickstart.model"作为包名。
- 添加"id"(int), "firstName"(String)和"lastName"(String)三个实例变量,并为它们加上setter/getter方法。
- 为你的类加上"@Entity"annotation,给"id" 加上 "@Id"和"@GeneratedValue" 注解
你的类如下:
package quickstart.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entitypublic class Person { @Id @GeneratedValue private Integer id; private String lastName; private String firstName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
@Entity让JPA服务Provider知道这个类可以被持久化。@Id标识"id"域为这个类的主键,@GeneratedValue使id域被提供者(Hibernate)自动生成。类和实例变量默认都被映射到同名的表和列上,详细情况请查看JPA文档。
Person service.
我们现在来写对"Person"对象进行CRUD操作的类。
- 创建一个接口,命名为"PersonService",包名为"quickstart.service"
2. 创建一个类,命名为"PersonServiceImpl",包名为"quickstart.service"package quickstart.service; import java.util.List; import quickstart.model.Person; public interface PersonService { public List<Person> findAll(); public void save(Person person); public void remove(int id); public Person find(int id); }
PersonServiceImpl.java
@PersistenceContext会让Spring在实例化的时候给服务注入一个EntityManager。@PersistenceContext注解可以放在实例变量,或者setter方法前面。如果一个类被注解为@Transactional,Spring将会确保类的方法在运行在一个事务中。package quickstart.service; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.springframework.transaction.annotation.Transactional; import quickstart.model.Person; @Transactional public class PersonServiceImpl implements PersonService { private EntityManager em; @PersistenceContext public void setEntityManager(EntityManager em) { this.em = em; } @SuppressWarnings("unchecked") public List<Person> findAll() { Query query = getEntityManager().createQuery("select p FROM Person p"); return query.getResultList(); } public void save(Person person) { if (person.getId() == null) { // new em.persist(person); } else { // update em.merge(person); } } public void remove(int id) { Person person = find(id); if (person != null) { em.remove(person); } } private EntityManager getEntityManager() { return em; } public Person find(int id) { return em.find(Person.class, id); } }
JPA 配置
- 在"src"目录下创建一个"META-INF"目录
- 在"META-INF"目录下创建一个名为"persistence.xml"的文件。
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="punit"> </persistence-unit></persistence>
JPA configuration can be set on this file. On this example it will be empty because the datasource configuration will be on the Spring configuration file. JPA的配置信息可以在这个文件中设置。本例中该文件为空,因为数据源(datasource)配置放在Spring的配置文件中。
Spring
- 更新/WebContent/WEB-INF/web.xml文件为以下内容:
<?xml version="1.0" encoding="UTF-8"?> <web-app id="person" 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"> <display-name>person</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> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> </web-app>
- 在/WebContent/WEB-INF目录下创建一个名为"applicationContext.xml"的文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> <bean id="personService" class="quickstart.service.PersonServiceImpl" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="MYSQL" /> <property name="showSql" value="true" /> </bean> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/test" /> <property name="username" value="root" /> <property name="password" value="root" /> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="personAction" scope="prototype" class="quickstart.action.PersonAction"> <constructor-arg ref="personService" /> </bean> </beans>
Struts
现在我们需要创建一个简单的Struts action,它封装了PersonServices的方法。并且我们配置Struts使用Spring作为对象工厂。
- 打开新建类对话框,输入"PersonAction"作为类名,包名为"quickstart.action",内容如下:
package quickstart.action; import java.util.List; import quickstart.model.Person; import quickstart.service.PersonService; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.Preparable; public class PersonAction implements Preparable { private PersonService service; private List<Person> persons; private Person person; private Integer id; public PersonAction(PersonService service) { this.service = service; } public String execute() { this.persons = service.findAll(); return Action.SUCCESS; } public String save() { this.service.save(person); this.person = new Person(); return execute(); } public String remove() { service.remove(id); return execute(); } public List<Person> getPersons() { return persons; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void prepare() throws Exception { if (id != null) person = service.find(id); } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
看,我的action是一个简单的POJO!
"Preparable"接口指示Struts去调用"prepare"方法,如果"PrepareInterceptor"被应用在action上(默认就是这样子的)。action的构造器有一个"PersonService"参数,在action被实例化的时候Spring会负责注入。
- 在"src"目录下创建一个名为"struts.xml"的文件,内容如下:
设置"struts.objectFactory"为"spring"会强制Struts使用Spring来实例化action,并注入所有定义在applicationContext.xml中的依赖关系。每个action别名的"class"属性被设置为"personAction",这也就是我们在applicationContext.xml中定义的PersonAction bean。要让Struts与Spring一起工作,我们仅仅需要做上面这点事情。<?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.objectFactory" value="spring" /> <constant name="struts.devMode"
value="true" /> <package name="person" extends="struts-default"> <action
name="list" method="execute" class="personAction"> <result>pages/list.jsp</result> <result
name="input">pages/list.jsp</result> </action> <action name="remove"
class="personAction" method="remove"> <result>pages/list.jsp</result> <result
name="input">pages/list.jsp</result> </action> <action name="save"
class="personAction" method="save">
<result>pages/list.jsp</result> <result name="input">pages/list.jsp</result> </action> </package></struts>
我们只有两个页面,"index.jsp"和"list.jsp"。"list.jsp"返回数据库中所有person的列表。我们把这个列表放到一个不同的页面上,是因为我们将要添加一点ajax来改进它。
- 在/WebContent/pages目录下创建一个名为"list.jsp" 的新文件,它的内容如下:
<%@ taglib prefix="s" uri="/struts-tags"%> <p>Persons</p> <s:if test="persons.size > 0"> <table> <s:iterator value="persons"> <tr id="row_<s:property value="id"/>"> <td> <s:property value="firstName" /> </td> <td> <s:property value="lastName" /> </td> <td> <s:url id="removeUrl" action="remove"> <s:param name="id" value="id" /> </s:url> <s:a href="%{removeUrl}" theme="ajax" targets="persons">Remove</s:a> <s:a id="a_%{id}" theme="ajax" notifyTopics="/edit">Edit</s:a> </td> </tr> </s:iterator> </table> </s:if>
当编辑链接被点击时,它会打开/edit主题,这将触发一个javascript函数来组装各个域。
- 在/WebContent目录下创建一个新文件"index.jsp",它的内容如下:
<%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <s:head theme="ajax" debug="true"/> <script type="text/javascript"> dojo.event.topic.subscribe("/save", function(data, type, request) { if(type == "load") { dojo.byId("id").value = ""; dojo.byId("firstName").value = ""; dojo.byId("lastName").value = ""; } }); dojo.event.topic.subscribe("/edit", function(data, type, request) { if(type == "before") { var id = data.split("_")[1]; var tr = dojo.byId("row_"+id); var tds = tr.getElementsByTagName("td"); dojo.byId("id").value = id; dojo.byId("firstName").value = dojo.string.trim(dojo.dom.textContent(tds[0])); dojo.byId("lastName").value = dojo.string.trim(dojo.dom.textContent(tds[1])); } }); </script> </head> <body> <s:url action="list" id="descrsUrl"/> <div style="width: 300px;border-style: solid"> <div style="text-align: right;"> <s:a theme="ajax" notifyTopics="/refresh">Refresh</s:a> </div> <s:div id="persons" theme="ajax" href="%{descrsUrl}" loadingText="Loading..." listenTopics="/refresh"/> </div> <br/> <div style="width: 300px;border-style: solid"> <p>Person Data</p> <s:form action="save" validate="true"> <s:textfield id="id" name="person.id" cssStyle="display:none"/> <s:textfield id="firstName" label="First Name" name="person.firstName"/> <s:textfield id="lastName" label="Last Name" name="person.lastName"/> <s:submit theme="ajax" targets="persons" notifyTopics="/save"/> </s:form> </div> </body> </html>
看,并没有页面刷新!
"personx" div会异步地装载它的内容,当请求被处理时显示"Loading..."提示(你可以使用"indicator"属性得到更佳的进度反馈),你可以通过点击"Refresh"链接来强制页面进行刷新。"submit"按钮,向action "save"提交一个异步的请求(PersonAction的"save"方法)
Validation验证
我们并不想我们的数据库存在任何的无名氏,所以我们给Form增加一点基本的客户端验证。在Struts2中,验证可以被放在xml文件里,命名模式为:ActionName-validation.xml,放在与action相同的包路径下。要给action的特定别名添加验证(比如方法),validation文件的命名必须为:ActionName-alias-validation.xml,这里的"alias"就是你的action的别名(这里也就是方法名,如"save")。在src/quickstart/action目录下添加一个名为"PersonAction-save-validation.xml"文件,它的内容如下:
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="person.firstName"> <field-validator type="requiredstring"> <message>First name is required!</message> </field-validator> </field> <field name="person.lastName"> <field-validator type="requiredstring"> <message>Last name is required!</message> </field-validator> </field> </validators>
关于现有的validator,以及如何编写和插入你自己的validator,请查看Struts文档
要运行项目,右击你的项目并选择Run AS -> Run on Server。你也可以通过相同的办法来调试它,右击项目 Debug As -> Debug on Server。下载并安装Struts 2的 showcase演示项目来查看更多的例子。
