一、新建項目
先在eclipse中新建一個maven項目,如下圖:
填寫id、version等,點finish。項目就建好了。下面是項目建好之后的樣子:
二、配置環境
1、添加依賴包
選中項目,右鍵——Maven2——Add Dependency,如下圖:
打開添加依賴包界面后添加包:struts2.0.11、spring2.0.6、hibernate-annotations3.2、hibernate-entitymanager3.2、hibernate3.2、servlet2.4、javax-persistence、mysql-connector-java5.0.4、spring-aop2.0.6、aspectjweaver1.5.3、struts2-spring-plugin2.0.11、junit、spring-mock2.0.6。
2、配置web.xml文件
在WEB-INF下新建文件web.xml
輸入如下內容:
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" 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>Struts 2</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.html</welcome-file> </welcome-file-list> </web-app>
用過struts1.x的人會發現這里沒有了servlet,而是一個filter,是的,這是struts2和struts1的一個不同之處。
在src/main/resource下新建文件struts.xml,這里是和struts1.x不一樣的地方。
輸入如下內容:
<?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.devMode" value="true" /> <package name="Struts2_MESSAGE" extends="struts-default" namespace="/message"> </package> </struts>
簡單解釋一下,constant的作用是自定義一些struts2的屬性,在struts2的包里帶了一個struts.properties文件,里邊設定了很多默認的struts2的運行參數,但是這些有時候是需要改變一下的,因此就提供了constant來覆蓋struts.properties中屬性。而package的作用可以簡單的看做把應用分模塊。package有個屬性叫namespace,上面我們配置為/message,假設這個package中還有一個名叫hello的action,那么我們訪問這個action的url就是:http://域名/message.hello.action。
本文的示例程序是一個簡單的留言添刪改查,所以取名叫message。
3、配置spring
在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/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"> </beans>
在web.xml文件中加入如下內容:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
用過spring的人應該知道這個listener的作用是啟動spring容器。
4、配置jpa和spring的jpa支持
在src/main/resource下新建文件夾META-INF,然后在這個文件夾中新建文件persistence.xml,輸入如下內容:
<?xml version="1.0" encoding="utf-8"?> <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="app-unit" transaction-type="RESOURCE_LOCAL"> </persistence-unit> </persistence>
可能有人會發現,怎么jpa的配置文件只有這么點了,這其實是因為我們用的spring的jpa支持,數據庫配置信息都放到spring的配置文件里了,后面就會看到。
然后打開前面新建的文件applicationContext.xml,在beans節點內添加如下內容:
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </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/message?createDatabaseIfNotExist=true" /> <property name="username" value="root" /> <property name="password" value="123" /> </bean> <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="generateDdl" value="false" /> <property name="showSql" value="true" /> </bean> </property> </bean>
看,數據庫配置信息出現了
這里的username和password需要根據你的實際情況修改。
5、集成struts2和spring
打開struts.xml文件,添加如下內容:
<constant name="struts.objectFactory" value="spring" /> <constant name="struts.objectFactory.spring.autoWire" value="type" />
到這里,配置基本完成。
三、新建域模型
我們先創建一個域模型Message,包括name、title、ip、content、inputTime等字段,代碼如下:
package com.struts.sample.domain; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Message { @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id; private String name; private String title; private String ip; private String content; private Date inputTime; getter and setter... }
@Entity是jpa的注解,表示這個類與一個表對應,如果沒有知名表明,那么就和message這個表對應,系統啟動是jpa會自動創建這個表。
@Id也是jpa注解,標識這個字段是主鍵。
@GeneratedValue(strategy = GenerationType.TABLE)是指定一個主鍵生成策略。
四、Service層的實現
接口就不帖出來了,直接把實現類寫出來。
package com.struts.sample.service; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import com.struts.sample.domain.Message; public class MessageServiceImpl implements MessageService { @PersistenceContext EntityManager em; public void setEm(EntityManager em) { this.em = em; } public List<Message> find(final String queryString, final int rowStartIdx, final int rowCount, final Object... values){ Query query = em.createQuery(queryString); if (values != null) for (int i = 0; i < values.length; i++) query.setParameter(i + 1, values[i]); if(rowStartIdx>0){ query.setFirstResult(rowCount); } if(rowCount>0){ query.setMaxResults(rowCount); } return query.getResultList(); } public void save(Message msg){ this.em.persist(msg); } public void del(Long id){ this.em.remove(this.get(id)); } public Message get(Long id){ return this.em.find(Message.class, id); } public void update(Message msg){ this.em.merge(msg); } public Message getBy(String field, Object value) { String queryString = "select msg from Message msg where msg."+field+"=:value"; Query query = this.em.createQuery(queryString).setParameter("value", value); List<Message> msgs = query.getResultList(); if(msgs.size()>1){ throw new java.lang.IllegalStateException( "worning --more than one object find!!"); }else if(msgs.size()==1){ return msgs.get(0); }else{ return null; } } }
注意這一句@PersistenceContext EntityManager em;容器啟動的時候會去創建messageService,創建的時候會發現這個注解:@PersistenceContext,然后容器會把EntityManager注入到這里。
接下來我們需要讓spring知道我們有這樣一個bean需要它來加載。打開配置文件applicationContext.xml,加上如下內容:
<bean id="messageService" class="com.struts.sample.service.MessageServiceImpl" />
現在我們來寫測試用例,spring為我們測試jpa提供了一個基類:org.springframework.test.jpa.AbstractJpaTests。
首先,我們在src/test/resource目錄下新建一個文件applicationContext.xml,內容與src/main/resource/applicationContext.xml的內容一樣。
接下來我們寫一個測試用例AbstractJpaTests ,代碼如下:
package com.struts.sample.service; import java.util.Date; import org.springframework.test.jpa.AbstractJpaTests; import com.struts.sample.domain.Message; public class MessageServiceImplTest extends AbstractJpaTests { @Override protected String[] getConfigLocations() { return new String[]{"classpath:applicationContext.xml"}; } private MessageService service; public void setService(MessageService service) { this.service = service; } public void testSave(){ Message msg = new Message(); msg.setContent("xxxxxxxxxxxxxxxxx"); msg.setName("tianyi"); msg.setInputTime(new Date()); msg.setTitle("www.easyjf.com"); msg.setIp("123.123.132.123"); service.save(msg); assertNotNull(msg.getId()); Message msg2 = service.getBy("name", "tianyi"); assertNotNull(msg); } }
運行這個測試程序,當你看到綠色的條條,說明你測試成功了。
五、Action層的實現
Service層已經實現并測試通過,接下來我們該寫Action了。Struts2支持POJO的action,不需要繼承或者實現任何類或者接口,可以很方便的測試。這個例子中我們就采用POJO式的action。代碼如下:
package com.struts.sample.action; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.struts.sample.domain.Message; import com.struts.sample.service.MessageService; public class MessageAction { private Message message; private Long id; private MessageService service; private List<Message> msgs = new ArrayList<Message>(); private Integer pages = 0; private Integer pageSize = 0; public List<Message> getMsgs() { return msgs; } public void setMsgs(List<Message> msgs) { this.msgs = msgs; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Integer getPages() { return pages; } public void setPages(Integer pages) { this.pages = pages; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Message getMessage() { return message; } public void setMessage(Message message) { this.message = message; } public String List(){ this.msgs = this.service.find("select msg from Message msg", pages, pageSize, null); return "success"; } public String Save(){ message.setInputTime(new Date()); if(message.getId()!=null){ this.service.update(message); }else{ this.service.save(message); } return this.List(); } public String Del(){ if(id==null){ return "error"; }else{ this.service.del(id); } return this.List(); } public String Edit(){ if(id==null){ return "error"; }else{ this.message = this.service.get(id); } return "success"; } public void setService(MessageService service) { this.service = service; } }
代碼很簡單,但是卻實現了添刪改查的功能,struts2確實比struts1好用多了。Struts2中可以將一個POJO作為action,就像上面的action。在用戶訪問到這個action的時候struts2會根據前臺提交進來的數據自動將必要的字段進行注入。
以save為例,當我們訪問到這個方法的時候,前臺表單域會有message.title、message.name、message.content等變量,struts2會根據這些前臺變量的名字將他們注入到MessageAction中的message中。而如果我們是執行的修改操作,那么前臺還會傳遞一個id到服務端,struts2又會自動將這個值注入到id中。
需要注意的是,如果你希望某個字段被注入,那么你應該提供一個setter,如果你同時希望這個字段能在頁面中被訪問,那么你還應該為它提供一個getter。
接下來我們來配置struts2,前面我們只是將struts2運行所需的環境配置起來了,現在我們需要配置新添加的Action。
打開struts.xml文件,在package節點中添加如下內容:
<action name="message_*" class="com.struts.sample.action.MessageAction" method="{1}"> <result>List.jsp</result> </action>
這個配置文件大家可能有點不明白,怎么還有“*”、“{1}”這種東西。現在我簡單解釋一下。struts2的action配置中支持通配符,message_*大家應該能明白,“*”就代表任意的東西,訪問的時候可以用message_aaa、message_bbb等,這些請求都會被交給MessageAction來處理,但是會不會有正確的結果還要看后面的method="{1}"這個東西。method屬性使得一個action中的一個方法就可以處理一個請求,這意味著我們不需要寫大量的action,而可以把一些相關的處理放到一個action中。現在我們來說說這個“{1}”,實際上“{1}”是代表前面name中的通配符的位置,struts會將第一個“*”代表的那一串字符串截取下來,作為method的值。在這個例子中,假設我們用message_aaa.action來訪問的話,那么struts會調用MessageAction的aaa方法。struts2的這種通配符可以支持多個,如果有兩個,那么"{1}"就代表第一個"*"所匹配的字符串,"{2}"就代表第二個"*"匹配的字符串,以此類推。
后臺的東西到這里就差不多了,剩下的就是頁面了。
六、頁面
是一個很簡單的示例,只有一個個頁面。在webapp目錄下新建目錄message,在message目錄下新建文件List.jsp,輸入如下內容:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>message</title> </head> <body> <s:iterator value="msgs"> <table style="border:1px solid #cccccc; margin-top:10px;" width="700"> <tr> <td>標題:<s:property value="title" /></td> <td>發表時間:<s:property value="inputTime" /></td> </tr> <tr> <td colspan="2" height="100" style="padding:10px;">留言內容: <div style="border:1px dotted #dddddd"> <p> <s:property value="content" /></p> </div></td> </tr> <tr> <td>作者:<s:property value="name" /></td> <td>操作:<a href='<s:url action="message_Edit"> <s:param name="id" value="id" /></s:url>'>Edit</a> <a href='<s:url action="message_Del"> <s:param name="id" value="id" /></s:url>'> Delete </a></td> </tr> </table> <hl/> </s:iterator> <s:form action="message_Save" > <s:hidden name="message.id" /> <s:textfield name="message.title" label="標題" /> <s:textarea name="message.content" label="內容" /> <s:textfield name="message.name" label="作者" /> <s:submit label="提交" /> </s:form> </body> </html>
到這里基本完工了,我們運行一下,看看結果。
打開瀏覽器輸入http://localhost:8080/message/message_List.action,居然是錯誤界面,錯誤信息如下:
這是為什么呢,原來我們沒有為service配置事務。打開applicationContext.xml,添加如下內容:
<aop:config> <aop:advisor pointcut="execution(* com.struts.sample.service.*.*(..))" advice-ref="txAdvice" /> </aop:config> <tx:advice id="txAdvice"> <tx:attributes> <tx:method name="insert*" /> <tx:method name="update*" /> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
運行試試,應該沒有問題了吧!運行界面如下:
們添加一條記錄看看,我隨便填了一條,點submit:
這里,我們的第一個示例就宣告完成了,如果有什么錯誤,還請大家指正。
源碼下載:struts-sample.rar
本文為原創,同時在easyjf官方博客發表:http://www.easyjf.com/blog/html/20080402/1474562.html。歡迎轉載,轉載請保留出處。