Hessian 是一個rpc框架, 我們需要先寫一個服務端, 然后在客戶端遠程的調用它即可。
?
服務端:
服務端通常和spring 做集成。
首先寫一個接口:
public interface HelloService {
void sayHello(String name);
}
然后一個實現,實現使用@Service("helloService") ? 實現spring bean注冊。
@Service("helloService")
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello " + name + "!");
}
}
spring xml配置中,通過org.springframework.remoting.caucho.HessianServiceExporter 完成服務的暴露:
<!-- Name保持與web.xml中的一致,web.xml下文中描述 -->
<bean name="HelloServiceExporter"
class="org.springframework.remoting.caucho.HessianServiceExporter">
<!-- service的ref與HelloServiceImpl中@Service中配置的一致 -->
<property name="service" ref="helloService" />
<!-- 接口的路徑 -->
<property name="serviceInterface"
value="hessian.HelloService" />
</bean>
web.xml 的關鍵配置:
<servlet>
<!-- servlet-name保持與spring-hessian.xml中一致 -->
<servlet-name>HelloServiceExporter</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServiceExporter</servlet-name>
<url-pattern>/HelloService</url-pattern>
</servlet-mapping>
HessianServiceExporter 有兩個關鍵的屬性: serviceInterface 和 service
serviceInterface 是必須是一個接口,也就是服務,值必須是一個接口的全路徑FQN
service 是具體的實現bean,是一個實例的引用
HttpRequestHandlerServlet extends HttpServlet
HttpRequestHandlerServlet 的關鍵代碼是:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LocaleContextHolder.setLocale(request.getLocale());
try {
this.target.handleRequest(request, response);
} catch (HttpRequestMethodNotSupportedException var8) {
String[] supportedMethods = var8.getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
}
response.sendError(405, var8.getMessage());
} finally {
LocaleContextHolder.resetLocaleContext();
}
}
其實就是調用了 HessianServiceExporter 的handler 方法,HessianServiceExporter 的關鍵代碼是:
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");
} else {
response.setContentType("application/x-hessian");
try {
this.invoke(request.getInputStream(), response.getOutputStream());
} catch (Throwable var4) {
throw new NestedServletException("Hessian skeleton invocation failed", var4);
}
}
}
代碼關系是:
HessianServiceExporter extends HessianExporter implements HttpRequestHandler
HessianExporter extends RemoteExporter implements InitializingBean
HessianExporter has a HessianSkeleton
HessianSkeleton extends AbstractSkeleton
HessianSkeleton 的invoke 方法實現關鍵的request處理
具體來說:
HessianInput extends AbstractHessianInput
HessianOutput extends AbstractHessianOutput
HessianInput 有提供 readHeader、readMethod、readMethodArgLength、readBytes、readInt、readLong、readString/readDouble/readObject、 readCall、 startCall、completeCall、readReply、startReply、completeReply 等方法
HessianOutput 有writeBytes Int long , Double,String, Object, writeReply 等方法
req 獲取 InputStream, res 獲取OutputStream。
HessianInput 從 req 中讀取 方法,參數, 然后method 反射調用 service, 然后通過 HessianOutput 寫回結果
in.completeCall();
out.writeReply(result); // 發送處理結果到 遠程客戶端
out.close();
這樣, 就完成了服務端的編寫,看起來還是比較簡單的。
客戶端:
因為Hessian服務端暴露的服務實際上是一個基于http實現通信的。 故我們需要通過http 調用?Hessian。
客戶端可以是一個web應用,也可以是一個簡單的main:
方式1,通過HessianProxyFactory:
public static void main(String[] args) {
try {
String url = "http://localhost:8080/HelloService";
HessianProxyFactory factory = new HessianProxyFactory();
HelloService helloService = (HelloService) factory.create(
HelloService.class, url);
helloService.sayHello("張三");
} catch (Exception e) {
e.printStackTrace();
}
}
稍微查看一下源碼,我們就會發現:
HessianProxy implements InvocationHandler, Serializable 可見 hessian 是使用jdk 動態代理實現的, 故我們需要一個接口
HessianURLConnection extends AbstractHessianConnection implements HessianConnection
HessianURLConnection 有一個 java.net.URLConnection ?, 可見Hessian 的通信主要就是通過http, 具體是 URLConnection實現的。
HessianProxy has a HessianProxyFactory
HessianProxyFactory has a HessianConnectionFactory
HessianProxyFactory 用來獲取conn: conn = this._factory.getConnectionFactory().open(this._url);
HessianProxy relate to HessianConnection
?
?具體來說是這樣的:
create 返回的是一個代理, return Proxy.newProxyInstance(loader, new Class[]{api, HessianRemoteObject.class}, handler);
HessianProxy 的invoke 是關鍵:
sendRequest 方法
os = conn.getOutputStream(); 通過conn 返回OutputStream
OutputStream os 強制轉換為?AbstractHessianOutput, 然后
out.call(methodName, args); // 發送數據到 遠程服務端
out.flush();
conn.sendRequest(); // 返回一個statusMessage?
is = httpConn.getInputStream(); // 通過conn獲取InputStream,
conn = this.sendRequest(mangleName, args);
is = conn.getInputStream(); // 通過sendRequest發送完數據后, 就可以通過conn 獲取遠程的返回的數據了。
InputStream的is 轉換為 AbstractHessianInput 后, 然后就可以 readObject
value = in.readObject(method.getReturnType()); // readObject 最終返回了 遠程調用的結果。 至此,單次 rpc 結束
?
?
方式2,通過HessianProxyFactoryBean:
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-servlet.xml");
HelloService ser = (HelloService) classPathXmlApplicationContext.getBean("testHessianService");
ser.sayHello("lk AA");
}
其實也就是把前面的HessianProxyFactory 集成到了spring,封裝成了bean
HessianProxy implements InvocationHandler, Serializable 可見 hessian 是使用jdk 動態代理實現的, 故我們需要一個接口
HessianURLConnection extends AbstractHessianConnection implements HessianConnection
HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object>
HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor
HessianClientInterceptor has a HessianProxyFactory
HessianClientInterceptor 定義hessianProxy:有一個屬性: private Class serviceInterface;
serviceInterface 定義serviceInterface:有一個屬性: private Class serviceInterface;
UrlBasedRemoteAccessor 定義serviceUrl:有一個屬性: private String serviceUrl;
hessianProxy 是通過proxyFactory(也就是HessianProxyFactory)創建, 可見, HessianProxyFactoryBean 還是通過HessianProxyFactory來完成的主要工作的。
簡單說,其實就是 spring 通過反射的調用proxyFactory 的遠程方法。
?
當然,實際使用的時候, 我們不會使用?ClassPathXmlApplicationContext, 它僅僅是在測試環境中使用。