基於springmvc的hessian調用原理淺析
一、客戶端
1、構造(初始化)
由客戶端的配置文件隨容器的啟動而進行初始化,配置文件如下:
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
${app_server_path}
com.avatarmind.server.service.ApplicationService
先放張類關係圖:
根據spring生命周期可知,容器初始化,會調用InitializingBean的afterPropertiesSet()方法,
而HessianProxyFactoryBean類實現了InitializingBean介面並重寫了afterPropertiesSet()方法。
所以主要流程為:
1)、HessianProxyFactoryBean的afterPropertiesSet()
2)、HessianClientInterceptor的afterPropertiesSet()
3)、HessianClientInterceptor的prepare()
4)、HessianClientInterceptor的createHessianProxy(HessianProxyFactory proxyFactory)
5)、HessianProxyFactory的create(Class> api, String urlName, ClassLoader loader)
public Object create(Class> api, URL url, ClassLoader loader)
{
if (api == null)
throw new NullPointerException("api must not be null for HessianProxyFactory.create()");
InvocationHandler handler = null;
handler = new HessianProxy(url, this, api);
return Proxy.newProxyInstance(loader,
new Class[] { api,
HessianRemoteObject.class },
handler);
}
整個流程最終就是為服務介面生成代理類。
2、使用介面調用服務
具體的方法調用我就不寫了,不知道的可以看我以前寫的hessian的基本使用。
由public class HessianProxy implements InvocationHandler, Serializable和構造的第 5)里的代碼可知
介面的調用會轉到HessianProxy的invoke(Object proxy, Method method, Object []args)方法里。
具體的代碼如下,有些細節我也不是很懂,好像就是序列化、發送請求(conn = sendRequest(mangleName, args);)、
反序列化,還有校驗和處理其他方法。
public Object invoke(Object proxy, Method method, Object []args)
throws Throwable
{
String mangleName;
synchronized (_mangleMap) {
mangleName = _mangleMap.get(method);
}
if (mangleName == null) {
String methodName = method.getName();
Class> []params = method.getParameterTypes();
// equals and hashCode are special cased
if (methodName.equals("equals")
&& params.length == 1 && params[0].equals(Object.class)) {
Object value = args[0];
if (value == null || ! Proxy.isProxyClass(value.getClass()))
return Boolean.FALSE;
Object proxyHandler = Proxy.getInvocationHandler(value);
if (! (proxyHandler instanceof HessianProxy))
return Boolean.FALSE;
HessianProxy handler = (HessianProxy) proxyHandler;
return new Boolean(_url.equals(handler.getURL()));
}
else if (methodName.equals("hashCode") && params.length == 0)
return new Integer(_url.hashCode());
else if (methodName.equals("getHessianType"))
return proxy.getClass().getInterfaces()[0].getName();
else if (methodName.equals("getHessianURL"))
return _url.toString();
else if (methodName.equals("toString") && params.length == 0)
return "HessianProxy[" + _url + "]";
if (! _factory.isOverloadEnabled())
mangleName = method.getName();
else
mangleName = mangleName(method);
synchronized (_mangleMap) {
_mangleMap.put(method, mangleName);
}
}
InputStream is = null;
HessianConnection conn = null;
try {
if (log.isLoggable(Level.FINER))
log.finer("Hessian[" + _url + "] calling " + mangleName);
// 調用服務端
conn = sendRequest(mangleName, args);
is = getInputStream(conn);
if (log.isLoggable(Level.FINEST)) {
PrintWriter dbg = new PrintWriter(new LogWriter(log));
HessianDebugInputStream dIs
= new HessianDebugInputStream(is, dbg);
dIs.startTop2();
is = dIs;
}
AbstractHessianInput in;
int code = is.read();
if (code == "H") {
int major = is.read();
int minor = is.read();
in = _factory.getHessian2Input(is);
Object value = in.readReply(method.getReturnType());
return value;
}
else if (code == "r") {
int major = is.read();
int minor = is.read();
in = _factory.getHessianInput(is);
in.startReplyBody();
Object value = in.readObject(method.getReturnType());
if (value instanceof InputStream) {
value = new ResultInputStream(conn, is, in, (InputStream) value);
is = null;
conn = null;
}
else
in.completeReply();
return value;
}
else
throw new HessianProtocolException(""" + (char) code + "" is an unknown code");
} catch (HessianProtocolException e) {
throw new HessianRuntimeException(e);
} finally {
try {
if (is != null)
is.close();
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
try {
if (conn != null)
conn.destroy();
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
}
}
二、服務端
1、構造(初始化)
配置文件:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
class="org.springframework.remoting.caucho.HessianServiceExporter">
同理:
主要流程:
1)、HessianExporter的afterPropertiesSet()
2)、HessianExporter的prepare()
3)、RemoteExporter的getProxyForService()
流程主要是生成HessianSkeleton類的對象。
因為配置文件的bean的name帶 / 符號,所以會被BeanNameUrlHandlerMapping進行映射處理。
2、被調用
由於基於mvc,所以請求會先到DispatcherServlet的doDispatch方法,然後根據路徑獲取handler,
因此服務的入口為HessianServiceExporter的handleRequest()方法。
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 驗證服務是否啟動成功的地方
if (!"POST".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
new String[] {"POST"}, "HessianServiceExporter only supports POST requests");
}
response.setContentType(CONTENT_TYPE_HESSIAN);
try {
// 調用
invoke(request.getInputStream(), response.getOutputStream());
}
catch (Throwable ex) {
throw new NestedServletException("Hessian skeleton invocation failed", ex);
}
}
流程:
1)、HessianServiceExporter的handleRequest()
2)、HessianExporter的invoke(InputStream inputStream, OutputStream outputStream)
3)、HessianExporter的doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)
核心代碼:skeleton.invoke(in, out);
4)、HessianSkeleton的invoke()
核心代碼:result = method.invoke(service, values);
利用反射調用具體的實現方法。中間摻雜著反序列化,校驗,序列化的其他方法。
更多優質內容推薦:
有錢任性,某公司豪擲500萬幫助20左右年輕人找工作,起因是做善良的人:
http://www.ujiuye.com/zt/jyfc/?wt.bd=zdy35845tt
學安卓,免學費!50天興趣課程等你來搶!
http://www.ujiuye.com/xydt/2017/13042.html?wt.bd=zdy35845tt


※「租賃」這頭豬,怎麼就趴上「共享」這個風口上了呢?
※CSS 樣式書寫規範
※nodejs 構建本地web測試伺服器 以及 解決訪問靜態資源的問題!
TAG:IT優就業 |
※necp_client_action系統調用中的堆溢出漏洞分析
※SpringCloud之服務提供與調用(Ribbon,Feign)
※使用 python 調用 echart 畫圖
※python調用api介面
※使用redis來調用iptables,封禁惡意IP
※透過現象看原理:詳解 Spring 中 Bean 的 this 調用導致 AOP 失效的原因
※c井動態調用WebService
※python訓練mask rcnn模型&&C++調用訓練好的模型——基於opencv4.0
※OpenCV調用TensorFlow是什麼意思
※dubbo+zipkin調用鏈監控
※通過調用Windows命令,將chm 文件轉換為html 文件
※Python 調用 Micro 宏自動解析 Nmon 文件進行數據歸檔
※關於如何使用webpack命令行傳入變數,並通過process.env來調用
※Asp.Net Core使用SignalR進行服務間調用
※jQuery UI 小部件(Widget)方法調用
※C/C++ 使用 TensorFlow 預訓練好模型——間接調用 Python 實現
※加入Transformer-XL,這個PyTorch包能調用各種NLP預訓練模型
※如何用Ptrace攔截並模擬Linux系統調用
※利用Zipkin追蹤Mysql資料庫調用鏈
※Alexa應用開發者將可免費調用8種Amazon Polly自然語音