天天看點

HttpInvoker簡單介紹以及使用

此文章來源:https://www.cnblogs.com/lpc-xx/p/8556827.html

一、Spring HTTP Invoker簡介

    Spring HTTP invoker 是 spring 架構中的一個遠端調用模型,執行基于 HTTP 的遠端調用(意味着可以通過防火牆),并使用 java 的序列化機制在網絡間傳遞對象。這需要在遠端和本地都使用Spring才行。用戶端可以很輕松的像調用本地對象一樣調用遠端伺服器上的對象,這有點類似于 webservice ,但又不同于 webservice ,差別如下:

WebService Http Invoker
跨平台,跨語言 隻支援 java 語言
支援 SOAP ,提供 wsdl 不支援
結構龐大,依賴特定的 webservice 實作,如 xfire等 結構簡單,隻依賴于 spring 架構本身

    說明:

    1. 伺服器端:通過 HTTP invoker 服務将服務接口的某個實作類提供為遠端服務

    2. 用戶端:通過 HTTP invoker 代理向伺服器端發送請求,遠端調用服務接口的方法

    3. 伺服器端與用戶端通信的資料均需要序列化

二、配置伺服器端和用戶端的步驟

配置伺服器端

1. 添加 springJAR 檔案

2. 建立相應的DTO(如果需要用到的話)

3. 建立服務接口

4. 建立服務接口的具體實作類

5. 公開服務

配置用戶端

1. 添加 springJAR 檔案

2. 建立相應的DTO(如果需要用到的話)

3. 建立服務接口

4. 通路服務

三、執行個體講解

配置伺服器端

先來個項目結構圖:

HttpInvoker簡單介紹以及使用

1). 添加 springJAR 檔案,這就不用說了,直接照着圖檔添加相應的類庫。

2). 建立服務接口和相應的DTO(Data Transmission Object)

這裡我們需要調用遠端的服務來查詢一個User對象,是以需要DTO啦。下面這個User類就是用于在網絡中傳輸的POJO類,也就是DTO啦,是以需要實作Serializable接口:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

package

com.abc.invoke.bean;

import

java.io.Serializable;

public

class

User 

implements

Serializable {

private

static

final

long

serialVersionUID = -6970967506712260305L;

private

String name;

private

int

age;

private

String email;

public

int

getAge() {

return

age;

}

public

void

setAge(

int

age) {

this

.age = age;

}

public

String getName() {

return

name;

}

public

void

setName(String name) {

this

.name = name;

}

public

String getEmail() {

return

email;

}

public

void

setEmail(String email) {

this

.email = email;

}

@Override

public

String toString() {

return

"User [name="

+ name + 

", age="

+ age + 

", email="

+ email + 

"]"

;

}

}

3). UserService是一個接口,裡面定義了服務的方法,這裡面的方法将會被用戶端調用:

1

2

3

4

5

6

7

package

com.abc.invoke.server.service;

import

com.abc.invoke.bean.User;

public

interface

UserService {

public

User getUserbyName(String name);

}

4). 建立服務接口的具體實作類。這裡的UserServiceImpl是實作了UserService方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package

com.abc.invoke.server.service.impl;

import

com.abc.invoke.bean.User;

import

com.abc.invoke.server.service.UserService;

public

class

UserServiceImpl 

implements

UserService {

public

User getUserbyName(String name) {

User u = 

new

User();

u.setName(name);

u.setEmail(

"[email protected]"

);

u.setAge(

20

);

return

u;

}

}

這裡面我沒有寫DAO等層面的東西,因為那些不是這篇文章要講述的内容,因而我隻是簡單的将傳給服務端的參數封裝到對象裡的一個字段就傳回了。

5). 公開服務

下面是web.xml檔案的内容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<?

xml

version

=

"1.0"

encoding

=

"UTF-8"

?>

<

web-app

xmlns:xsi

=

"http://www.w3.org/2001/XMLSchema-instance"

xmlns

=

"http://java.sun.com/xml/ns/javaee"

xmlns:web

=

"http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 

http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

<

display-name

>SpringInvoke</

display-name

>

<

servlet

>

<

servlet-name

>service</

servlet-name

>

<

servlet-class

>org.springframework.web.servlet.DispatcherServlet</

servlet-class

>

<

init-param

>

<

param-name

>contextConfigLocation</

param-name

>

<

param-value

>classpath:service-servlet.xml</

param-value

>

</

init-param

>

<

load-on-startup

>1</

load-on-startup

>

</

servlet

>

<

servlet-mapping

>

<

servlet-name

>service</

servlet-name

>

<

url-pattern

>/service/*</

url-pattern

>

</

servlet-mapping

>

<!-- 其實下面這個welcome-file-list沒啥用,我留着隻是為了在起好Tomcat後不會報一個404而已 -->

<

welcome-file-list

>

<

welcome-file

>index.html</

welcome-file

>

</

welcome-file-list

>

</

web-app

>

這裡我們使用/service作為service的字首,那麼用戶端請求調用時需要加上這個字首,比如:

http://{host}:{port}/InvokeServer/service/{serviceName}

裡面用到的service-servlet檔案:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<?

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:context

=

"http://www.springframework.org/schema/context"

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">

<!-- 這個Bean映射了當URL是/userService時,處理器為userServiceInvoker -->

<

bean

class

=

"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"

>

<

property

name

=

"mappings"

>

<

props

>

<

prop

key

=

"/userService"

>userServiceInvoker</

prop

>

</

props

>

</

property

>

</

bean

>

<!-- Announce that this interface is a HTTP invoker service. -->

<

bean

id

=

"userServiceInvoker"

class

=

"org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"

>

<

property

name

=

"service"

ref

=

"userServiceImpl"

/>

<

property

name

=

"serviceInterface"

value

=

"com.abc.invoke.server.service.UserService"

/>

</

bean

>

<

bean

id

=

"userServiceImpl"

class

=

"com.abc.invoke.server.service.impl.UserServiceImpl"

/>

</

beans

>

注意:
  1. <prop key="/userService">userServiceInvoker</prop>中的/userService是請求的服務的URL中的一部分,就是說這樣的URL會被userServiceInvoker處理
  2. 這裡将com.abc.invoke.server.service.UserService映射給了com.abc.invoke.server.service.impl.UserServiceImpl類了。

到此為止,伺服器算是配置好了,接下來開始配置用戶端。

配置用戶端

先來看看項目結構圖:

HttpInvoker簡單介紹以及使用

1). 添加 springJAR 檔案,這也不用說了,直接照着圖檔添加相應的類庫。

2). 建立服務接口和相應的DTO。

特别注意:這個類和Server端聲明的DTO要一樣,包名和字段名都要一樣才行。因為用戶端發起請求查詢User,服務端處理後先将User序列化後在傳回給用戶端,而用戶端拿到這個User後需要将其反序列化。如果包名或者字段名不同,則會被認為是不同的對象,會反序列化失敗,調用也就出錯了。我之前就是将User類的包名寫得不一樣(User類的包名在服務端為com.abc.invoke.server.bean,而在用戶端則為com.abc.invoke.client.bean),報了以下錯誤:

1

2

3

4

5

6

7

8

9

Exception in thread 

"main"

org.springframework.remoting.RemoteAccessException: 

Could not deserialize result from HTTP invoker remote service [http:

//localhost:8080/InvokeServer/service/userService]; 

nested exception is java.lang.ClassNotFoundException: com.abc.invoke.server.bean.User

at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.convertHttpInvokerAccessException(HttpInvokerClientInterceptor.java:

208

)

at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java:

145

)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:

172

)

at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:

202

)

at com.sun.proxy.$Proxy0.getUserbyName(Unknown Source)

at com.abc.invoke.client.Test.main(Test.java:

14

)

很明顯可以看出,Could not deserialize result from HTTP invoker remote service......,就是因為Server端與Client端的DTO的包名不同導緻反序列化失敗。

3). 建立服務接口

這也沒啥好說的,接口和Server端定義的一樣就行,不一樣肯定報錯。可以直接将DTO和接口定義的類拷貝到用戶端即可。這個接口将會被看做是用戶端和服務端通信的“契約”。

4). 通路服務

來看看application-context.xml:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<?

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:context

=

"http://www.springframework.org/schema/context"

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">

<!-- 用戶端使用 HttpInvokerProxyFactoryBean 代理用戶端向伺服器端發送請求,請求接口為 UserService 的服務 -->

<

bean

id

=

"userService"

class

=

"org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"

>

<

property

name

=

"serviceUrl"

value

=

"http://localhost:8080/InvokeServer/service/userService"

/>

<

property

name

=

"serviceInterface"

value

=

"com.abc.invoke.client.service.UserService"

/>

</

bean

>

</

beans

>

這裡使用了org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean這個類來作為一個service的代理類。注意到serviceUrl屬性為http://localhost:8080/InvokeServer/service/userService (當然,我在本機啟動的服務端并在本機通過main函數調用service,我在另一台機器上運作Test類的main函數,調用結果正确)。這個localhost:8080應改為實際的IP位址和端口。),這個URL的位址以/service開始,是以會被Server端攔截下來,而URL中的 /userService則為service路徑,該路徑與在Server端中service-servlet.xml中聲明的

1

<

prop

key

=

"/userService"

>userServiceInvoker</

prop

>

路徑一緻,是以這個調用會被userServiceInvoker處理。

最後再寫一個簡單的測試類Test.java:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

package

com.abc.invoke.client;

import

org.springframework.context.ApplicationContext;

import

org.springframework.context.support.ClassPathXmlApplicationContext;

import

com.abc.invoke.bean.User;

import

com.abc.invoke.client.service.UserService;

public

class

Test {

public

static

void

main(String[] args) {

ApplicationContext ac = 

new

ClassPathXmlApplicationContext(

"classpath:application-context.xml"

);

UserService service = (UserService)ac.getBean(

"userService"

);

User u = service.getUserbyName(

"Alvis"

);

System.out.println(u);

}

}

這個類也很簡單,就是從Spring的Context中取出了定義的userService這個Bean(這其實就是服務端service的一個代理類),然後直接調用該service的方法獲得結果并列印。

到此為止,用戶端配置完成。

四、啟動服務并測試

直接在項目InvokeServer上啟動Tomcat,可以看到路徑/userService的處理者是userServiceInvoker:

HttpInvoker簡單介紹以及使用

下面是遠端調用的執行結果:

HttpInvoker簡單介紹以及使用

從結果中可以看到,我代碼裡寫的名字叫Alvis,用用戶端調用服務端的service後,傳回的對象中名字是用戶端設定的名字,測試成功。

這裡是項目源代碼,供需要的朋友參考。

參考頁面:http://hanqunfeng.iteye.com/blog/868210

繼續閱讀