Spring 3提供了對RESTful Web服務的支持。 在本教程中,我們將向您展示如何在Spring中實現RESTful Web服務 ,或者如何將現有的Spring服務公開為RESTful Web服務 。 為了使事情變得更有趣,我們將從上一篇關于Spring GWT Hibernate JPA Infinispan HornetQ集成的文章的 開頭繼續 。 我們將使用GWTSpringInfinispanHornetQ項目,并將“ employeeService” CRUD功能公開為RESTful Web服務 。 當然,您可以參考本文,只是了解如何將Spring服務公開為RESTful Web服務 。
實現RESTful Web服務的最流行的方法是Sun的JAX-RS規范。 有幾個支持JAX-RS的項目,例如CXF , Jersey , RESTEasy和Restlet 。 他們中的大多數人還提供Spring支持。 Spring不直接支持JAX-RS ,而是將RESTful功能添加到了Spring MVC本身。 如果您不熟悉Spring MVC框架,請在此處查閱Spring文檔的相應章節。 對于不耐煩的人,下面簡要概述。
Spring MVC代表模型視圖控制器。 它有助于構建靈活且松散耦合的Web應用程序。 模型-視圖-控制器設計模式可確保多層Web應用程序中關注點(業務邏輯,表示邏輯和導航邏輯)的分離。 “控制器”負責接收來自用戶的請求并調用后端服務。 模型負責封裝應用程序數據。 視圖使用模型對象將響應呈現給用戶。 簡而言之 :
將請求發送到Spring MVC Framework時,將發生以下事件序列。
- “ DispatcherServlet”首先收到請求
- “ DispatcherServlet”查詢“ HandlerMapping”并調用與請求關聯的“ Controller”
- “控制器”通過調用適當的服務方法來處理請求,并將“ ModeAndView”對象返回到“ DispatcherServlet”。 “ ModeAndView”對象包含模型數據和視圖名稱
- “ DispatcherServlet”將視圖名稱發送到“ ViewResolver”以查找要調用的實際“視圖”
- “ DispatcherServlet”將模型對象傳遞給“視圖”以呈現結果
- 在模型數據的幫助下,“視圖”呈現結果并將其返回給用戶
聊夠了! 讓我們弄臟雙手!
我們將需要“ cglib”字節碼生成庫和“ asm”字節碼操作框架,以便Spring能夠將AOP方面正確地應用于“ Controller”對象。 我們將使用“CGLIB” 2.2版本,您可以下載在這里和在“asm”二進制分發版,你可以下載在這里 。 在“ asm”二進制發行版的/ lib / all文件夾下找到asm-all-3.3.jar,并將asm-all-3.3.jar和cglib-2.2.jar都放置在項目的/ war / WEB-INF / lib文件夾下。
最后,我們將需要Jackson JSON處理器。 我們將使用1.5.3版的“核心”和“映射器”發行版,您可以從此處下載。 將兩個jackson-core-asl-1.5.3.jar
和項目的/ war / WEB-INF / lib文件夾下的jackson-mapper-asl-1.5.3.jar。
我們必須照顧我們的Eclipse項目的依賴性。 以下jar應包含在項目的Java構建路徑中:
- org.springframework.web-3.0.1.RELEASE-A.jar
如上所述,“ DispatcherServlet”是一個用于管理整個請求處理過程的servlet。 與其他任何servlet一樣,它需要在Web部署描述符或我們的應用程序中進行配置。 在/ war / WEB-INF文件夾下找到“ web.xml”文件,然后添加以下內容:
<servlet><servlet-name>dispatcher</servlet-name><servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class><load-on-startup>2</load-on-startup>
</servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/restServices/*</url-pattern>
</servlet-mapping>
默認情況下,“ DispatcherServlet”將查找名為“ {servlet-name} –servlet.xml”的文件以加載Spring MVC配置。 在我們的例子中是“ dispatcher-servlet.xml”。 在這里,我們將url –模式用作“ / restServices / *”,以便“ DispatcherServlet”僅處理指定模式下的所有傳入請求。 創建一個“ dispatcher–servlet.xml”文件,并將其放在/ war / WEB-INF文件夾下,如下所示:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"><context:component-scan base-package="com.javacodegeeks.gwtspring.server.endpoints" /><tx:annotation-driven /><mvc:annotation-driven /></beans>
這里要注意的事情:
- 我們將“ context:component-scan”配置元素的base-package屬性設置為我們的Spring MVC注釋類將駐留的位置
- 我們使用“ tx:annotation-driven”配置元素,以便能夠向我們的MVC類注入事務行為
- “ mvc:annotation-driven”是Spring 3的配置元素,可大大簡化Spring MVC的設置。 該標簽注冊了將請求分派到@Controller注釋類的所需的“ HandlerMapping”和“ HandlerAdapter”。 另外,它根據您的類路徑中存在的內容應用合理的默認值。 這樣的默認值包括(包括其他):
- 支持使用@NumberFormat注釋格式化數字字段
- 如果類路徑上有Joda Time,則支持使用@DateTimeFormat批注格式化Date,Calendar和Joda Time字段
- 如果類路徑上有JSR-303提供者,則支持使用@Valid注釋驗證@Controller注釋的類輸入
- 如果類路徑上包含JAXB ,則支持讀寫XML
- 如果杰克遜在類路徑上,則支持讀寫JSON
在項目的“服務器”包下創建一個“端點”子包。 就GWT而言,服務端點是服務器端組件,因此所有類都必須放在“服務器”包下。 在“端點”子包下,放置“ EmployeeServiceController”類,如下所示:
package com.javacodegeeks.gwtspring.server.endpoints;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
import com.javacodegeeks.gwtspring.shared.services.EmployeeService;@Controller
@RequestMapping("/employeeService")
public class EmployeeServiceController {@AutowiredEmployeeService employeeService;@RequestMapping(value = "/{id}", method = RequestMethod.GET)@ResponseBodypublic EmployeeDTO findEmployee(@PathVariable("id") long employeeId) {return employeeService.findEmployee(employeeId);}@RequestMapping(value = "/{id}/{name}/{surname}/{job}", method = RequestMethod.POST)@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public String saveEmployee(@PathVariable("id") long employeeId, @PathVariable String name, @PathVariable String surname, @PathVariable("job") String jobDescription) throws Exception {employeeService.saveEmployee(employeeId, name, surname, jobDescription);return "redirect:/restServices/employeeService/" + employeeId;}@RequestMapping(value = "/{id}/{name}/{surname}/{job}", method = RequestMethod.PUT)@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public String updateEmployee(@PathVariable("id") long employeeId, @PathVariable String name, @PathVariable String surname, @PathVariable("job") String jobDescription) throws Exception {employeeService.updateEmployee(employeeId, name, surname, jobDescription);return "redirect:/restServices/employeeService/" + employeeId;}@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)@ResponseBodypublic String deleteEmployee(@PathVariable("id") long employeeId) throws Exception {employeeService.deleteEmployee(employeeId);return "OK";}}
我們提供“ EmployeeService”和“ EmployeeDTO”實現作為參考:
package com.javacodegeeks.gwtspring.server.services;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import com.javacodegeeks.gwtspring.server.dao.EmployeeDAO;
import com.javacodegeeks.gwtspring.server.utils.NotificationsProducer;
import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
import com.javacodegeeks.gwtspring.shared.services.EmployeeService;@Service("employeeService")
public class EmployeeServiceImpl implements EmployeeService {@Autowiredprivate EmployeeDAO employeeDAO;@AutowiredNotificationsProducer notificationsProducer;@PostConstructpublic void init() throws Exception {}@PreDestroypublic void destroy() {}public EmployeeDTO findEmployee(long employeeId) {return employeeDAO.findById(employeeId);}@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public void saveEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception {EmployeeDTO employeeDTO = employeeDAO.findById(employeeId);if(employeeDTO == null) {employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription);employeeDAO.persist(employeeDTO);}}@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public void updateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception {EmployeeDTO employeeDTO = employeeDAO.findById(employeeId);if(employeeDTO != null) {employeeDTO.setEmployeeName(name);employeeDTO.setEmployeeSurname(surname);employeeDTO.setJob(jobDescription);}}@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public void deleteEmployee(long employeeId) throws Exception {EmployeeDTO employeeDTO = employeeDAO.findById(employeeId);if(employeeDTO != null)employeeDAO.remove(employeeDTO);}@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public void saveOrUpdateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception {EmployeeDTO employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription);employeeDAO.merge(employeeDTO);notificationsProducer.sendNotification("Save Or Update Employee with values : \nID : " + employeeId + "\nName : " + name + "\nSurname : " + surname + "\nJob description : " + jobDescription);}}
如您所見,“ EmployeeServiceController”充當“ employeeService”類的包裝器類。 我們參考了實際的服務,并實現了直接調用服務功能的方法。 要將具有“控制器”功能的“ EmployeeServiceController”類注入,我們只需對其進行注釋即可。 @Controller注釋指定被注釋的類是“ Controller”類,可用于“ DispatcherServlet”將請求委托給它。
在本教程的開始,我們談到了“ DispatcherServlet”如何使用“ HandlerMappings”來選擇適當的“ Controllers”以委派用戶請求。 @RequestMapping批注用于將Web請求映射到特定的處理程序類和/或處理程序方法。 因此,通過在類型級別使用@RequestMapping(“ / employeeService”)批注,我們指示“ DispatcherServlet”將“ / employeeService”資源URI下的所有Web請求委托給“ EmployeeServiceController”實例。 此外,我們在方法級別使用@RequestMapping批注,以根據請求的資源URI將“ DispatcherServlet”委托范圍縮小到特定操作。 您應該已經注意到@PathVariable注釋的使用。 在Spring 3中,引入了通過@PathVariable注釋使用URI模板的方法。 URI模板是一個URI –類似于字符串,包含一個或多個變量名。 當這些變量代替值時,模板將成為URI。 因此,對“ / employeeService / 1”資源URI的客戶端HTTP GET請求將委派給我們的“ EmployeeServiceController”實例的“ findEmployee”操作,并且“ employeeId”參數的值將設置為1。
注意:僅當分派器中存在相應的“ HandlerMapping”(用于類型級別的注釋)和/或“ HandlerAdapter”(用于方法級別的注釋)時,才會處理@RequestMapping。 由于Spring 3 MVC的簡化,在“ dispatcher-servlet.xml”中使用“ mvc:annotation-driven”配置元素可以滿足我們的所有配置需求。
在本教程的開始,我們談到了“控制器”如何通過調用適當的服務方法來處理每個請求并將“ ModeAndView”對象返回到“ DispatcherServlet”。 “ ModeAndView”對象包含模型數據和視圖名稱,以便正確地呈現給客戶端。 這種行為并不總是令人滿意的。 例如,在我們的示例中,我們想將服務響應序列化到HTTP響應主體。 可以將@ResponseBody批注放在方法上,并指示將返回類型直接寫到HTTP響應主體(而不是放置在Model中或解釋為視圖名稱)。 根據客戶端接受的內容類型(信息來自客戶端請求的“ Accept” HTTP Header字段),我們將返回服務回復的XML或JSON表示形式。 為此,我們通過Spring OXM模塊和Jackson JSON處理器使用JAXB編組器和反編組器。
最后,您應該注意到“ saveEmployee”和“ updateEmployee”操作。 這兩個沒有@ResponseBody批注,并返回“ redirect:/ restServices / employeeService / + employeeId”字符串。 “ Controller”方法可以返回“特殊” String值,該值會向“ DispatcherServlet”發出命令。 使用上述重定向命令,“ DispatcherServlet”會將調用重定向到與指定URI資源關聯的“ Controller”方法(在我們的示例中為“ findEmployee”操作)。 因此,當客戶端發出“ saveEmployee”或“ updateEmployee”命令時,將收到剛剛插入或更新的“ employeeDTO”對象的XML或JSON表示作為答復。
下面我們介紹DTO類。
package com.javacodegeeks.gwtspring.shared.dto;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;@Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
@Entity
@XmlRootElement
@Table(name = "EMPLOYEE")
public class EmployeeDTO implements java.io.Serializable {private static final long serialVersionUID = 7440297955003302414L;@Id@Column(name="employee_id")private long employeeId;@Column(name="employee_name", nullable = false, length=30)private String employeeName;@Column(name="employee_surname", nullable = false, length=30)private String employeeSurname;@Column(name="job", length=50)private String job;public EmployeeDTO() {}public EmployeeDTO(int employeeId) {this.employeeId = employeeId; }public EmployeeDTO(long employeeId, String employeeName, String employeeSurname,String job) {this.employeeId = employeeId;this.employeeName = employeeName;this.employeeSurname = employeeSurname;this.job = job;}public long getEmployeeId() {return employeeId;}public void setEmployeeId(long employeeId) {this.employeeId = employeeId;}public String getEmployeeName() {return employeeName;}public void setEmployeeName(String employeeName) {this.employeeName = employeeName;}public String getEmployeeSurname() {return employeeSurname;}public void setEmployeeSurname(String employeeSurname) {this.employeeSurname = employeeSurname;}public String getJob() {return job;}public void setJob(String job) {this.job = job;}
}
這里唯一需要注意的是,我們已經用@XmlRootElement注釋對DTO類進行了注釋,以便被JAXB編組器正確編組。
而已! 要部署Web應用程序,只需將/ war文件夾復制到Apache – Tomcat “ webapps”文件夾中。 您可以將war文件夾的名稱更改為自己喜歡的名稱,最好在項目名稱后重命名,例如GWTSpringInfinispanHornetQRemoting
在午餐之前,應用程序不要忘記創建數據庫模式,這里是“ javacodegeeks”。
在此示例中,我們使用了Apache Derby數據庫版本10.6.1.0。 您可以在此處下載二進制發行版。 在發行版的/ lib目錄下找到derby.jar并將其放置在項目的/ war / WEB-INF / lib目錄下。 要使用Spring配置嵌入式數據庫,請在/ war / WEB-INF目錄下的applicationContext.xml中添加以下配置代碼:
<bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close"><property name="uniqueResourceName" value="javacodegeeks" /><property name="xaDataSourceClassName" value="org.apache.derby.jdbc.EmbeddedXADataSource" /><property name="xaProperties"><props><prop key="databaseName">javacodegeeks</prop><prop key="createDatabase">create</prop></props></property><property name="maxPoolSize" value="50" /><property name="minPoolSize" value="20" />
</bean>
您可以下載RESTClient并測試“ employeeService”的REST接口,如下所示:
- 發出“ http:// localhost:8080 / GWTSpringInfinispanHornetQRemoting / restServices / employeeService / 1 / myName / mySurname / myJob”的POST請求,然后應檢索新創建的“ employeeDTO”的XML表示形式,如下所示:
<?xml version="1.0" encoding="UTF-8" standalone="yes">
<employeeDTO><employeeId>1</employeeId><employeeName>myName</employeeName><employeeSurname>mySurname</employeeSurname><job>myJob</job>
</employeeDTO>
- 對http:// localhost:8080 / GWTSpringInfinispanHornetQRemoting / restServices / employeeService / 1發出GET請求,通過POST操作將收到相同的結果
- 發出PUT請求,并從“ employeeDTO”對象更新您喜歡的任何字段。 該服務將以更新的“ employeeDTO” XML表示形式進行回復
- 對http:// localhost:8080 / GWTSpringInfinispanHornetQRemoting / restServices / employeeService / 1發出DELETE請求,您將收到“確定”答復!
- 在添加HTTP標頭指令以將接受的內容類型定義為“ application / json”后,發出上述命令。 返回的“ employeeDTO”表示應如下所示:
{"employeeId":1,"employeeName":"myName","employeeSurname":"mySurname","job":"myJob"}
您可以從此處下載該項目(如開頭所述,并且不包含先前的文章,所需的第三方庫)
玩得開心!
賈斯汀
- GWT 2 Spring 3 JPA 2 Hibernate 3.5教程
- GWT Spring和Hibernate進入數據網格世界
- Spring 3 HornetQ 2.1集成教程
- GWT 2 Spring 3 JPA 2 Hibernate 3.5教程– Eclipse和Maven 2展示
- 帶有Spring和Maven教程的JAX–WS
翻譯自: https://www.javacodegeeks.com/2010/06/spring-3-restful-web-services.html