1.Spring中除了提供HTTP調用器方式的遠程調用,還對第三方的遠程調用實現提供了支持,其中提供了對Hessian的支持。html
Hessian是由Caocho公司發佈的一個輕量級的二進制協議遠程調用實現方案,Hessian也是基於HTTP協議的,其工做原理以下:java
(1).客戶端:spring
a.發送遠程調用請求:服務器
客戶端程序—>發送遠程調用請求—>Hessian客戶端攔截器—>封裝遠程調用請求—>Hessian代理—>經過HTTP協議發送遠程請求代理到服務端。app
b.接收遠程調用響應:框架
遠程調用結果—>HTTP響應—>客戶端。ide
(1).服務端:源碼分析
a.接收遠程調用請求:ui
遠程調用HTTP請求—>HessianServiceExporter接收請求—>HessianExporter將遠程調用對象封裝爲HessianSkeleton框架—> HessianSkeleton處理遠程調用請求。this
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/serviceUrl</value>
</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<Object> {
//遠程調用代理對象
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響應中返回,源碼以下:
[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。