課程網站:
相關文章:
Restful 服務端編程
Restful簡介
RPC(Remote Procedure Call Protocol)溪食,即遠程過程調用僧诚,是一種進程之間通信的技術萍鲸。在一開始厢破,電腦的各個進程之間是無法通訊的景醇。為了讓進程之間能夠互相通訊微饥,于是就發(fā)明了IPC(Inter-process communication颅眶,單機中運行的進程之間的相互通信)技術都哭。隨著互聯(lián)網的出現(xiàn)啊易,人們又將IPC擴展成為了RPC吁伺,于是實現(xiàn)了不同機器之間進程的通訊。
但是租谈,實現(xiàn)RPC是很麻煩的篮奄。為了讓RPC變得簡單,程序員發(fā)明了各種各樣的框架割去。Restful就是其中一類高效以及簡潔易用的架構風格窟却。
REST是一種架構風格,其核心是面向資源呻逆,REST專門針對網絡應用設計和開發(fā)方式夸赫,以降低開發(fā)的復雜性,提高系統(tǒng)的可伸縮性咖城。REST提出設計概念和準則為:
1. 網絡上的所有事物都可以被抽象為資源(resource)
2. 每一個資源都有唯一的資源標識(resource identifier)茬腿,對資源的操作不會改變這些標識
3. 所有的操作都是無狀態(tài)的
REST簡化開發(fā)呼奢,其架構遵循CRUD原則,該原則告訴我們對于資源(包括網絡資源)只需要四種行為:創(chuàng)建切平,獲取握础,更新和刪除就可以完成相關的操作和處理。您可以通過統(tǒng)一資源標識符(Universal Resource Identifier悴品,URI)來識別和定位資源禀综,并且針對這些資源而執(zhí)行的操作是通過 HTTP 規(guī)范定義的。其核心操作只有GET,PUT,POST,DELETE苔严。
由于REST強制所有的操作都必須是stateless的定枷,這就沒有上下文的約束,如果做分布式邦蜜,集群都不需要考慮上下文和會話保持的問題依鸥。極大的提高系統(tǒng)的可伸縮性。
對于SOAP Webservice和Restful Webservice的選擇問題悼沈,首先需要理解就是SOAP偏向于面向活動贱迟,有嚴格的規(guī)范和標準,包括安全絮供,事務等各個方面的內容衣吠,同時SOAP強調操作方法和操作對象的分離,有WSDL文件規(guī)范和XSD文件分別對其定義壤靶。而REST強調面向資源缚俏,只要我們要操作的對象可以抽象為資源即可以使用REST架構風格。
參考資料:
http://blog.csdn.net/lilongsheng1125/article/details/41643665
https://www.zhihu.com/question/25536695
CXF Restful Service 基礎
在下載程序包贮乳,例如最新的apache-cxf-3.1.7-src.zip忧换。然后解壓到自己喜歡的文件夾。例如我是放在了桌面向拆。
這里面的文件特別多亚茬。我們要找的是一個sample。我們打開神奇的IntelliJ IDEA浓恳,然后import project刹缝。找到apache-cxf-3.1.7-src\distribution\src\main\release\samples\jax_rs\basic這個路徑。然后雙擊pom.xml颈将。然后一直按next就可以打開這個項目了梢夯。
然后和上次一樣,在右邊找到框晴圾,輸入命令:
- install:安裝颂砸,你會看到制品放入本地倉庫
- -Pserver: 運行服務器,程序會自動啟動 jetty
- -Pclient: 運行客戶端程序
我們只需要分別運行 install 和 -Pserver。 然后沾凄,在瀏覽器中輸入 URL:http://localhost:9000/customerservice/customers/123 梗醇。我們就看到了運行的結果。
返回的是萬能的xml撒蟀。于是簡單的程序體驗就到這里了。
CXF 與 spring 集成
maven創(chuàng)建web項目:選擇maven-archetype-webapp温鸽,然后輸入:
groupId: me.cxf-sample
artifactId: cxf-rs-spring
項目名為cxf-rs-spring保屯。
然后我們創(chuàng)建在main文件夾里面創(chuàng)建java文件夾,然后把它設置為Source Root涤垫。接著加入Java包姑尺,命名為demo.jaxrs.server。
然后我們將剛剛Demo中的Customer.java, CustomerService.java, Product.java, Order.java拖入到該包蝠猬。
這個時候切蟋,發(fā)現(xiàn)沒有rs包。
我們添加依賴:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
按系統(tǒng)文檔配置src/main/webapp/WEB-INF/web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>cxf-rs-spring Maven Webapp</display-name>
<!-- 設置Spring容器加載配置文件路徑 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beans.xml</param-value>
</context-param>
<!-- 加載Spring容器配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- 加載CXFServlet容器配置 -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
再在同一文件夾下榆芦,創(chuàng)建beans.xml柄粹,然后繼續(xù)配置
<?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:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<!-- do not use import statements if CXFServlet init parameters link to this beans.xml -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxrs:server id="customerService" address="/service1">
<jaxrs:serviceBeans>
<ref bean="customerBean" />
</jaxrs:serviceBeans>
</jaxrs:server>
<bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
</beans>
最后,我們配置pom.xml匆绣。然后導入需要的包驻右。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.cxf-sample</groupId>
<artifactId>cxf-rs-spring</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>cxf-rs-spring Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- 申明常用軟件的版本 -->
<properties>
<jettyVersion>9.3.7.v20160115</jettyVersion>
<cxf.version>3.1.6</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<!-- CXF jaxrs 依賴 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- CXF jaxrs description 依賴 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- spring 依賴 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>cxf-rs-spring</finalName>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jettyVersion}</version>
</plugin>
</plugins>
</build>
</project>
最后用jetty:run
啟動服務器。然后崎淳,在瀏覽器輸入:http://localhost:8080/與http://localhost:8080/service1/customerservice/customers/123 看效果就行了堪夭。
如果我們輸入http://localhost:8080/service1?_wadl 就可以看到該服務接口的定義。
我做的時候拣凹,出現(xiàn)了下面這個問題森爽,服務器啟動不了。找了很久嚣镜,原來是之前搞apache的時候爬迟,服務器沒有關閉。于是關閉服務器就好了祈惶。
[INFO] Jetty server exiting.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.110s
[INFO] Finished at: Tue Sep 27 22:13:51 CST 2016
[INFO] Final Memory: 25M/249M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.3.7.v20160115:run (default-cli) on project cxf-rs-spring: Failure: Address already in use: bind -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
JAX-RS 服務端編程
將cxf-rs-spring文件夾復制出來雕旨,改名為cxf-rs-spring-test。然后進入pom.xml捧请,將原來<artifactId>cxf-rs-spring</artifactId>
改為<artifactId>cxf-rs-spring-test</artifactId>
凡涩。
然后我們用IntelliJ IDEA打開這個文件。在java文件夾下疹蛉,創(chuàng)建org.me.examples.helloworld活箕,然后在這個包下面新建Hello.java。
使用下面代碼:
package org.me.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@Path("/hello/{username}")
public class Hello {
@GET
@Produces("text/xml")
public String sayHello(@PathParam("username") String userName) {
return "hello " + userName;
}
}
再將beans.xml文件下的關鍵部分稍作修改可款。
<jaxrs:server id="customerService" address="/service1">
<jaxrs:serviceBeans>
<ref bean="customerBean" />
<ref bean="Hello" />
</jaxrs:serviceBeans>
</jaxrs:server>
<bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
<bean id="Hello" class="org.me.examples.helloworld.Hello" />
然后我們使用命令行育韩,輸入命令:curl http://localhost:8080/service1/hello/Monkey
克蚂。
效果如下:
返回了“hello Monkey”,說明成功了筋讨。
習題部分
RESTful Webservice 作為遠程調用(RPC)埃叭,它的輸入是什么?輸入是什么悉罕?
REST核心是URL和面向資源赤屋,用URL代替了原來復雜的操作方法,把它簡化成了GET,PUT,POST,DELETE四個核心操作壁袄。一個URL對應著一個資源类早。因此我們可以認為,輸入是一段URL嗜逻,輸出是一段資源(例如Json或者xml)涩僻。
有一個 Java 遠程函數(shù)聲明 public Customer getCustomer(String id),把 HTTP 協(xié)議輸入/輸出映射到該函數(shù)栈顷,至少要申明(annotation)哪些信息逆日?請寫出這些 annotation,并簡單解釋協(xié)議與函數(shù)的關系妨蛹。
@GET
@Path("/customers/{id}/")
修改 實驗1 的 @Path 為 @Path(“/myservice/”)屏富,請用 curl -v 給出結果
我們將@Path("/customerservice/")改為@Path(“/myservice/”)。我們知道蛙卤,@Path("/customerservice/")表示項目根訪問該類的URI路徑狠半。如果我們將其換成myservice,那么URL也要換成http://localhost:9000/myservice/customers/123颤难。同理神年,如果我們換的是@Path("/customers/…")部分,對應的URL也要改變行嗤。否則就會顯示404已日。實驗結果如下:
簡述 GET 與 POST 的區(qū)別
Http獲取資源的核心操作有四個:GET,POST栅屏,PUT飘千,DELETE,對應著對這個資源的查栈雳,改护奈,增,刪4個操作哥纫。因此原理上霉旗,GET方法得到資源是不能修改信息的,而POST代表資源有可能會被修改。
在表面上厌秒,GET和POST的區(qū)別表現(xiàn)在以下方面:
- GET請求的數(shù)據會附在URL之后(就是把數(shù)據放置在HTTP協(xié)議頭中)读拆,POST把提交的數(shù)據則放置在是HTTP包的包體中。
- GET方式提交的數(shù)據最多只能是1024字節(jié)(因為GET用URL提交數(shù)據鸵闪,所有關鍵在于瀏覽器對URL的支持)檐晕,理論上POST沒有限制,可傳較大量的數(shù)據(實際為了安全起見蚌讼,也有對應的限制)棉姐。
- 在用不同技術編程的時候,GET和POST的使用方法不一樣啦逆。
- POST的安全性要比GET的安全性高。比如:通過GET提交數(shù)據笛洛,用戶名和密碼將明文出現(xiàn)在URL上夏志,因為(1)登錄頁面有可能被瀏覽器緩存,(2)其他人查看瀏覽器的歷史紀錄苛让,那么別人就可以拿到你的賬號和密碼了沟蔑,除此之外,使用GET提交數(shù)據還可能會造成Cross-site request forgery攻擊狱杰。
參考資料:
http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html
在實際應用中 Map<Long, Customer> customers 使用會產生副作用嗎瘦材?為什么?
在多線程編程的時候仿畸,會產生副作用食棕。有可能兩個用戶同時請求同一個ID。因此要盡量避免這種情況错沽。
Spring IOC 與 DI 是 java 編程核心內容之一簿晓。閱讀Spring IoC 使用詳解用自己的語言解釋 IoC 解耦原理。
IoC通過依賴注入千埃,使用set或者構造方法實現(xiàn)了解耦憔儿。就是本來要修改一個接口的實現(xiàn),必須要修改里面的內容放可,用依賴注入谒臼,就只需要set一下,就可以了耀里。
(r阽汀)使用 Maven 構建(6)給出的案例,項目名稱 IOC_test
……
在 Spring 試驗中备韧, 閱讀 beans.xml劫樟, 寫出申明根資源的關鍵內容
<jaxrs:server id="customerService" address="/service1">
<jaxrs:serviceBeans>
<ref bean="customerBean" />
</jaxrs:serviceBeans>
</jaxrs:server>
<bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
(!)嘗試寫一個根資源類 Hello,用 GET 方法實現(xiàn) String SayHello() 遠程服務叠艳。請給出 Hello類代碼奶陈,增加根資源的配置(不是修改)XML內容,curl測試結果附较。
package org.me.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@Path("/hello/{username}")
public class Hello {
@GET
@Produces("text/xml")
public String sayHello(@PathParam("username") String userName) {
return "hello " + userName;
}
}
<jaxrs:server id="customerService" address="/service1">
<jaxrs:serviceBeans>
<ref bean="customerBean" />
<ref bean="Hello" />
</jaxrs:serviceBeans>
</jaxrs:server>
<bean id="customerBean" class="demo.jaxrs.server.CustomerService" />
<bean id="Hello" class="org.me.examples.helloworld.Hello" />
WADL 的全稱吃粒。從程序結果返回中截取一段 WADL 內容。
WADL全稱為Web Application Description Language拒课,即Web 應用程序描述語言徐勃。
輸出對象格式協(xié)商是Resquest的Header段的哪個 Field 決定的?
Accept:application/xml
JAX-RS 的全稱
JAX-RS即Java API for RESTful Web Services早像,是一個Java 編程語言的應用程序接口僻肖,支持按照表述性狀態(tài)轉移(REST)架構風格創(chuàng)建Web服務。
@Produces 和 @Consumes 的作用
@Produces:
用于指定輸出格式卢鹦,參數(shù)可以是字符串數(shù)組臀脏。 如果是一個數(shù)組,第一個是默認輸出格式冀自。如果客戶端指定輸出揉稚,則會匹配其他輸出。 例如:
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Consumes:
指定處理輸入媒體的類型熬粗,例如 “Content-Type: application/json”搀玖。 如果你的服務中函數(shù)中帶一個自由的參數(shù),規(guī)定用合適的 輸入 provider 驻呐,使用 body 中內容灌诅,實例化這個參數(shù)。例如:
@POST
@Consumes("text/plain")
@Produces("text/xml")
public String sayHello(@PathParam("username") String userName, String letter) {
return "hello " + userName + ":" + letter;
}
(1┦稀)請完成 實驗3 的每個參數(shù)處理的內容延塑,然后在jesery上完成類似工作。請描述兩個平臺是否完全兼容答渔?簡述不兼容的內容关带。
……