1.Spring中除了提供HTTP調用器方式的遠程調用,還對第三方的遠程調用實現提供了支持,其中提供了對Hessian的支持。
Hessian是由Caocho公司發布的一個輕量級的二進制協議遠程調用實現方案,Hessian也是基於HTTP協議的,其工作原理如下:
(1).客戶端:
a.發送遠程調用請求:
客戶端程序—>發送遠程調用請求—>Hessian客戶端攔截器—>封裝遠程調用請求—>Hessian代理—>通過HTTP協議發送遠程請求代理到服務端。
b.接收遠程調用響應:
遠程調用結果—>HTTP響應—>客戶端。
(1).服務端:
a.接收遠程調用請求:
遠程調用HTTP請求—>HessianServiceExporter接收請求—>HessianExporter將遠程調用對象封裝為HessianSkeleton框架—> HessianSkeleton處理遠程調用請求。
b.返回遠程調用響應:
HessianSkeleton封裝遠程調用處理結果—>HTTP響應—>客戶端。
本文章通過分析Spring對Hessian支持的相關源碼,了解Spring對Hessian支持的具體實現。
2.Hessian的客戶端配置:
Hessian的客戶端需要做類似如下的配置:
[xhtml] view plaincopy
-
<bean id=”hessianProxy” class=”org.springframework.remoting.caucho.HessianProxyFactoryBean”>
-
<property name=”serviceUrl”>
-
<value>http://hostAddress:8080/serviceUrlvalue>
-
property>
-
<property name=”serviceInterface”>
-
<value>遠程調用服務接口value>
-
]property>
-
bean>
和HTTP調用器的配置類似,都需要配置遠程調用請求的url,這個url要和服務端的url一致,Spring通過DispatcherServlet找到服務端對於的請求url。
HessianProxyFactoryBean是Spring中管理Hessian客戶端的IoC容器,主要負責產生服務端遠程調用代理和對客戶端遠程調用的攔截器設置。
3.HessianProxyFactoryBean:
HessianProxyFactoryBean生成遠程調用代理和客戶端遠程調用攔截器設置,其源碼如下:
[java] view plaincopy
public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean {
-
//遠程調用代理對象
-
private Object serviceProxy;
-
//Spring IoC容器依賴注入完成后的回調方法
-
public void afterPropertiesSet() {
-
//首先調用父類HessianClientInterceptor的回調方法
-
super.afterPropertiesSet();
-
//創建遠程調用代理對象並設置攔截器,注意這個this參數,因為//HessianProxyFactoryBean繼承HessianClientInterceptor,因此其本身也
-
//是Hassien客戶端攔截器
-
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
-
}
-
//Spring IoC容器的接口FactoryBean產生對象的方法,客戶端通過該方法獲取被管
-
//理的遠程調用代理
-
public Object getObject() {
-
return this.serviceProxy;
-
}
-
//獲取對象的類型
-
public Class getObjectType() {
-
return getServiceInterface();
-
}
-
//對象是否是單態類型,Spring默認管理的對象都是單態模式
-
public boolean isSingleton() {
-
return true;
-
}
-
}
HessianProxyFactoryBean最核心的功能就是在IoC容器回調方法中產生遠程調用代理對象,在產生遠程調用代理對象時,將代理對象的攔截器設置為其父類HessianClientInterceptor。
4.HessianClientInterceptor攔截客戶端的遠程調用請求:
HessianClientInterceptor對客戶端的遠程調用進行攔截,為客戶端的遠程調用創建Hessian代理,通過Hessian代理調用服務端遠程調用對象,其源碼如下:
[java] view plaincopy
-
public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {
-
//創建Hessiann代理工廠
-
private HessianProxyFactory proxyFactory = new HessianProxyFactory();
-
//Hessian代理
-
private Object hessianProxy;
-
//設置Hessian代理工廠
-
public void setProxyFactory(HessianProxyFactory proxyFactory) {
-
this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());
-
}
-
//設置Hessian序列化工廠
-
public void setSerializerFactory(SerializerFactory serializerFactory) {
-
this.proxyFactory.setSerializerFactory(serializerFactory);
-
}
-
//設置Hessian是否發送java集合類型對象
-
public void setSendCollectionType(boolean sendCollectionType) {
-
this.proxyFactory.getSerializerFactory().setSendCollectionType(sendCollectionType);
-
}
-
//設置遠程調用時是否重載方法
-
public void setOverloadEnabled(boolean overloadEnabled) {
-
this.proxyFactory.setOverloadEnabled(overloadEnabled);
-
}
-
//設置遠程調用用戶名
-
public void setUsername(String username) {
-
this.proxyFactory.setUser(username);
-
}
-
//設置遠程調用密碼
-
public void setPassword(String password) {
-
this.proxyFactory.setPassword(password);
-
}
-
//設置是否使用Hessian的Debug調試模式
-
public void setDebug(boolean debug) {
-
this.proxyFactory.setDebug(debug);
-
}
-
//設置是否使用chunked端口發送Hessian請求
-
public void setChunkedPost(boolean chunkedPost) {
-
this.proxyFactory.setChunkedPost(chunkedPost);
-
}
-
//設置Hessian等待響應的超時時長
-
public void setReadTimeout(long timeout) {
-
this.proxyFactory.setReadTimeout(timeout);
-
}
-
//設置是否使用Hessain版本2協議解析請求和響應
-
public void setHessian2(boolean hessian2) {
-
this.proxyFactory.setHessian2Request(hessian2);
-
this.proxyFactory.setHessian2Reply(hessian2);
-
}
-
//設置是否使用Hessian版本2協議解析請求
-
public void setHessian2Request(boolean hessian2) {
-
this.proxyFactory.setHessian2Request(hessian2);
-
}
-
//設置是否使用Hessian版本2協議解析響應
-
public void setHessian2Reply(boolean hessian2) {
-
this.proxyFactory.setHessian2Reply(hessian2);
-
}
-
//子類HessianProxyFactoryBean的回調方法調用此回調方法
-
public void afterPropertiesSet() {
-
//調用其父類UrlBasedRemoteAccessor的回調方法獲取客戶端配置的請求url
-
super.afterPropertiesSet();
-
//初始化Hessian代理
-
prepare();
-
}
-
//初始化Hessian代理
-
public void prepare() throws RemoteLookupFailureException {
-
try {
-
//創建Hessian代理
-
this.hessianProxy = createHessianProxy(this.proxyFactory);
-
}
-
catch (MalformedURLException ex) {
-
throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
-
}
-
}
-
//創建Hessian代理
-
protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {
-
Assert.notNull(getServiceInterface(), "'serviceInterface' is required");
-
//使用Hessian代理工廠創建Hessian代理
-
return proxyFactory.create(getServiceInterface(), getServiceUrl());
-
}
-
//攔截器客戶端請求的方法
-
public Object invoke(MethodInvocation invocation) throws Throwable {
-
if (this.hessianProxy == null) {
-
throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +
-
"invoke 'prepare' before attempting any operations");
-
}
-
//獲取當前環境中線程類加載器
-
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
-
try {
-
//調用Hessian代理的方法,是Hessian遠程調用的入口方法,使用JDK反射機制
-
return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());
-
}
-
//處理Hessian遠程調用中的異常
-
catch (InvocationTargetException ex) {
-
Throwable targetEx = ex.getTargetException();
-
if (targetEx instanceof InvocationTargetException) {
-
targetEx = ((InvocationTargetException) targetEx).getTargetException();
-
}
-
if (targetEx instanceof HessianConnectionException) {
-
throw convertHessianAccessException(targetEx);
-
}
-
else if (targetEx instanceof HessianException || targetEx instanceof HessianRuntimeException) {
-
Throwable cause = targetEx.getCause();
-
throw convertHessianAccessException(cause != null ? cause : targetEx);
-
}
-
else if (targetEx instanceof UndeclaredThrowableException) {
-
UndeclaredThrowableException utex = (UndeclaredThrowableException) targetEx;
-
throw convertHessianAccessException(utex.getUndeclaredThrowable());
-
}
-
else {
-
throw targetEx;
-
}
-
}
-
catch (Throwable ex) {
-
throw new RemoteProxyFailureException(
-
"Failed to invoke Hessian proxy for remote service [" + getServiceUrl() + "]", ex);
-
}
-
//重置類加載器
-
finally {
-
resetThreadContextClassLoader(originalClassLoader);
-
}
-
}
-
//將Hessian異常轉換為Spring遠程調用異常
-
protected RemoteAccessException convertHessianAccessException(Throwable ex) {
-
if (ex instanceof HessianConnectionException || ex instanceof ConnectException) {
-
return new RemoteConnectFailureException(
-
"Cannot connect to Hessian remote service at [" + getServiceUrl() + "]", ex);
-
}
-
else {
-
return new RemoteAccessException(
-
"Cannot access Hessian remote service at [" + getServiceUrl() + "]", ex);
-
}
-
}
-
}
通過上面對HessianClientInterceptor的源碼分析,我們可以看到Hessian客戶端攔截器提供的最重要的方法是對遠程調用攔截的方法invoke,在該方法中使用JDK的反射機制調用Hessian代理對象的指定方法。而Hessian代理是由Hessain代理器工廠HessianProxyFactory產生的,這個Hessian代理器工廠是有Hessian提供的。
5.Hessian服務器端配置:
在Hessian的服務端需要進行類似如下的配置:
[xhtml] view plaincopy
-
<bean id=”/serviceUrl” class=”org.springframework.remoting.caucho.HessianServiceExporter”>
-
<property name=”service”>
-
<ref bean=”service”/>
-
property>
-
<property name=”serviceInterface”>
-
<value>遠程服務接口value>
-
property>
-
bean>
Spring的HessianServiceExporter把遠程調用服務整合到Spring MVC框架中,通過DispatcherServlet將客戶端請求轉發到服務器端相應的請求url遠程對象上。
注意:serviceUrl要和客戶端serviceUrl中配置的相同,service即是服務端提供服務的遠程對象。
6.HessianServiceExporter處理Hessian遠程調用請求:
HessianServiceExporter接收客戶端的遠程調用請求,並調用HessianExporter具體處理遠程調用,並且遠程調用結果封裝到HTTP響應中返回,e家裝網源碼如下:
[java] view plaincopy
-
public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {
-
//處理Hessian請求,並將處理結果封裝為Hessian響應返回
-
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-
//Hessian只支持HTTP的POST方法
-
if (!"POST".equals(request.getMethod())) {
-
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
-
new String[] {"POST"}, "HessianServiceExporter only supports POST requests");
-
}
-
//設置Hessian響應內容類型為:application/x-hessian
-
response.setContentType(CONTENT_TYPE_HESSIAN);
-
try {
-
//HessianExporter真正處理Hessian請求和封裝Hessian響應的方法
-
invoke(request.getInputStream(), response.getOutputStream());
-
}
-
catch (Throwable ex) {
-
throw new NestedServletException("Hessian skeleton invocation failed", ex);
-
}
-
}
-
}
7.HessianExporter處理Hessian請求並將結果封裝為HTTP響應:
[java] view plaincopy
-
public class HessianExporter extends RemoteExporter implements InitializingBean {
-
//Hessian HTTP響應內容類型
-
public static final String CONTENT_TYPE_HESSIAN = "application/x-hessian";
-
//Hessian序列化工廠
-
private SerializerFactory serializerFactory = new SerializerFactory();
-
//Hessian Debug日志
-
private Log debugLogger;
-
//Hessian服務端框架
-
private HessianSkeleton skeleton;
-
//設置Hessian序列化工廠
-
public void setSerializerFactory(SerializerFactory serializerFactory) {
-
this.serializerFactory = (serializerFactory != null ? serializerFactory : new SerializerFactory());
-
}
-
//設置序列化集合發送java集合類型
-
public void setSendCollectionType(boolean sendCollectionType) {
-
this.serializerFactory.setSendCollectionType(sendCollectionType);
-
}
-
//設置Hessian調試模式
-
public void setDebug(boolean debug) {
-
this.debugLogger = (debug ? logger : null);
-
}
-
//回調方法
-
public void afterPropertiesSet() {
-
prepare();
-
}
-
//初始化Hessian服務框架
-
public void prepare() {
-
//這里調用父類RemoteExporter的方法檢查服務提供類、服務接口
-
checkService();
-
checkServiceInterface();
-
//創建遠程服務類的Hessian框架
-
this.skeleton = new HessianSkeleton(getProxyForService(), getServiceInterface());
-
}
-
//遠程調用處理入口
-
public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {
-
Assert.notNull(this.skeleton, "Hessian exporter has not been initialized");
-
doInvoke(this.skeleton, inputStream, outputStream);
-
}
-
//遠程調用處理方法
-
protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)
-
throws Throwable {
-
//獲取類加載器
-
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
-
try {
-
InputStream isToUse = inputStream;
-
OutputStream osToUse = outputStream;
-
//設置Hessian調試日志
-
if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) {
-
PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger));
-
HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter);
-
dis.startTop2();
-
HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter);
-
dos.startTop2();
-
isToUse = dis;
-
osToUse = dos;
-
}
-
if (!isToUse.markSupported()) {
-
isToUse = new BufferedInputStream(isToUse);
-
isToUse.mark(1);
-
}
-
int code = isToUse.read();
-
int major;
-
int minor;
-
AbstractHessianInput in;
-
AbstractHessianOutput out;
-
//根據客戶端不同的Hessian版本,設置不同的Hessian抽象輸入/輸出
-
//Hessian2.0
-
if (code == 'H') {
-
major = isToUse.read();
-
minor = isToUse.read();
-
if (major != 0x02) {
-
throw new IOException("Version " + major + "." + minor + " is not understood");
-
}
-
in = new Hessian2Input(isToUse);
-
out = new Hessian2Output(osToUse);
-
in.readCall();
-
}
-
//Hessian2.0
-
else if (code == 'C') {
-
isToUse.reset();
-
in = new Hessian2Input(isToUse);
-
out = new Hessian2Output(osToUse);
-
in.readCall();
-
}
-
//Hessian1.0
-
else if (code == 'c') {
-
major = isToUse.read();
-
minor = isToUse.read();
-
in = new HessianInput(isToUse);
-
if (major >= 2) {
-
out = new Hessian2Output(osToUse);
-
}
-
else {
-
out = new HessianOutput(osToUse);
-
}
-
}
-
else {
-
throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code);
-
}
-
//設置Hessian序列化工廠
-
if (this.serializerFactory != null) {
-
in.setSerializerFactory(this.serializerFactory);
-
out.setSerializerFactory(this.serializerFactory);
-
}
-
try {
-
//通過服務端遠程對象的Hessian框架處理遠程調用
-
skeleton.invoke(in, out);
-
}
-
finally {
-
try {
-
in.close();
-
isToUse.close();
-
}
-
catch (IOException ex) {
-
}
-
try {
-
out.close();
-
osToUse.close();
-
}
-
catch (IOException ex) {
-
}
-
}
-
}
-
//重置類加載器
-
finally {
-
resetThreadContextClassLoader(originalClassLoader);
-
}
-
}
-
}
通過上面對HessianExporter源碼分析,我們看到真正進行遠程調用處理的是由Hessian提供的服務端HessianSkeleton。