1. HessianServlet的init方法,创建接口的类对象和接口的实现类对象,并初始化HessianSkeleton对象。
2. service方法内则是调用HessianSkeleton对象的invoke方法处理请求
1. 创建HessianProxyFactory,调用create方法获取到方法结果
一、简介
Hessian是一个使用二进制传输的服务框架,一种轻便的RPC框架,是基于HTTP协议的。
因为,Hessian是基于HTTP协议的,那么就会有一个Web服务(Hessian服务端),消费端通过获取代理对象来调用服务端的方法,
服务端和客户端都需要依赖接口.
二、使用
- 公共服务接口类:中间工程jar包middleProject,仅仅包含hello方法和一个重新设置用户年龄的方法。
- 服务端:构建成一个web服务,只有一个接口实现类需要依赖middleProject,需要配置成hessian服务。
- 客户端:同样依赖middleProject,使用hessian代理工厂实例化公共接口服务类,然后调用该实例的方法。
maven项目作为案例,zy_client是客户端,zy_server是服务端,zy_interface是接口工程
创建接口
public interface HelloService {
String sayHello(String str);
}
Server
server服务端是一个web项目,发布服务。
1. 引入接口依赖和Hessian依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
<!-- 接口的依赖省略... -->
2. 实现接口
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String str) {
return "hello"+str;
}
}
3. web.xml 中配置HessianServlet
<servlet>
<servlet-name>hessianServlet</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-class</param-name>
<param-value>cn.bing.service.impl.HelloServiceImpl</param-value>
</init-param>
<init-param>
<param-name>home-api</param-name>
<param-value>cn.bing.service.HelloService</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>hessianServlet</servlet-name>
<url-pattern>/hessian</url-pattern>
</servlet-mapping>
4. 将web启动起来,服务的地址是 http://localhost:8080/hessian
Client
1. 引入接口和Hessian的依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
<!-- 接口的依赖省略 -->
2. 调用服务,通过hessian的代理工厂生成helloService实例,注意到如果传递的数据是pojo类,需要将pojo实现序列化接口,
本例中是String类型数据。
public class RemoteTest {
public static void main(String[] args) throws Exception{
String url = "http://localhost:8080/hello/hessian";
HessianProxyFactory factory = new HessianProxyFactory();
HelloService helloService = (HelloService) factory.create(HelloService.class, url);
String str = helloService.sayHello("world!");
System.out.println(str);
}
}
三、原理
服务端源码浅析
1. HessianServlet的init方法,创建接口的类对象和接口的实现类对象,并初始化HessianSkeleton对象。
这个类的父类是AbstractSkeleton,初始化的过程中,会以方法名称和method对象存入 _methodMap 中,后续接受到请求,从这个map中取出method对象处理。
public void init(ServletConfig config)
throws ServletException
{
super.init(config);
try {
if (_homeImpl != null) {
}
else if (getInitParameter("home-class") != null) {
String className = getInitParameter("home-class");
Class<?> homeClass = loadClass(className);
_homeImpl = homeClass.newInstance();
init(_homeImpl);
}
else if (getInitParameter("service-class") != null) {
String className = getInitParameter("service-class");
Class<?> homeClass = loadClass(className);
_homeImpl = homeClass.newInstance();
init(_homeImpl);
}
else {
if (getClass().equals(HessianServlet.class))
throw new ServletException("server must extend HessianServlet");
_homeImpl = this;
}
if (_homeAPI != null) {
}
else if (getInitParameter("home-api") != null) {
String className = getInitParameter("home-api");
_homeAPI = loadClass(className);
}
else if (getInitParameter("api-class") != null) {
String className = getInitParameter("api-class");
_homeAPI = loadClass(className);
}
else if (_homeImpl != null) {
_homeAPI = findRemoteAPI(_homeImpl.getClass());
if (_homeAPI == null)
_homeAPI = _homeImpl.getClass();
_homeAPI = _homeImpl.getClass();
}
//..................
_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
//................
}
2. service方法内则是调用HessianSkeleton对象的invoke方法处理请求
public void service(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (! req.getMethod().equals("POST")) {
res.setStatus(500); // , "Hessian Requires POST");
PrintWriter out = res.getWriter();
res.setContentType("text/html");
out.println("<h1>Hessian Requires POST</h1>");
return;
}
String serviceId = req.getPathInfo();
String objectId = req.getParameter("id");
if (objectId == null)
objectId = req.getParameter("ejbid");
ServiceContext.begin(req, res, serviceId, objectId);
try {
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
response.setContentType("x-application/hessian");
SerializerFactory serializerFactory = getSerializerFactory();
invoke(is, os, objectId, serializerFactory);
} catch (RuntimeException e) {
throw e;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException(e);
} finally {
ServiceContext.end();
}
}
3. HessianSkeleton的invoke方法
public void invoke(InputStream is, OutputStream os,
SerializerFactory serializerFactory)
上面的的方法核心的是调用它的重载方法处理,这个方法才是Hessian服务端处理请求的核心方法.
方法的主要参数:
serivce: servlet初始化时候就实例化的实现类对象
in : 输入流,获取请求中的方法名称和参数列表
out: 输出流,将结果写回到客户端
public void invoke(Object service,
AbstractHessianInput in,
AbstractHessianOutput out)
throws Exception
{
ServiceContext context = ServiceContext.getContext();
// backward compatibility for some frameworks that don't read
// the call type first
in.skipOptionalCall();
// Hessian 1.0 backward compatibility
String header;
while ((header = in.readHeader()) != null) {
Object value = in.readObject();
context.addHeader(header, value);
}
String methodName = in.readMethod();
int argLength = in.readMethodArgLength();
Method method;
method = getMethod(methodName + "__" + argLength);
if (method == null)
method = getMethod(methodName);
if (method != null) {
}
else if ("_hessian_getAttribute".equals(methodName)) {
String attrName = in.readString();
in.completeCall();
String value = null;
if ("java.api.class".equals(attrName))
value = getAPIClassName();
else if ("java.home.class".equals(attrName))
value = getHomeClassName();
else if ("java.object.class".equals(attrName))
value = getObjectClassName();
out.writeReply(value);
out.close();
return;
}
else if (method == null) {
out.writeFault("NoSuchMethodException",
escapeMessage("The service has no method named: " + in.getMethod()),
null);
out.close();
return;
}
Class<?> []args = method.getParameterTypes();
if (argLength != args.length && argLength >= 0) {
out.writeFault("NoSuchMethod",
escapeMessage("method " + method + " argument length mismatch, received length=" + argLength),
null);
out.close();
return;
}
Object []values = new Object[args.length];
for (int i = 0; i < args.length; i++) {
// XXX: needs Marshal object
values[i] = in.readObject(args[i]);
}
Object result = null;
try {
result = method.invoke(service, values);
} catch (Exception e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException)
e1 = ((InvocationTargetException) e).getTargetException();
log.log(Level.FINE, this + " " + e1.toString(), e1);
out.writeFault("ServiceException",
escapeMessage(e1.getMessage()),
e1);
out.close();
return;
}
// The complete call needs to be after the invoke to handle a
// trailing InputStream
in.completeCall();
out.writeReply(result);
out.close();
}
invoke的方法处理流程:
1. 从请求中获取方法名称和方法的参数
2. 从_methodMap中查出对应的method对象,调用method对象的invoke方法处理得到方法的处理结果
3. 输出流将方法的处理结果写回客户端。
Hessian客户端源码浅析
1. 创建HessianProxyFactory,调用create方法获取到方法结果
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);
}
方法内部通过Proxy创建一个代理对象,代理对象的核心是在handler的实现上,看下handler的实现 HessianProxy
public class HessianProxy implements InvocationHandler, Serializable {
代理对象的方法调用的核心是调用handler的invoke方法,接下来,看下invoke的实现。
2. HessianProxy的核心实现invoke方法
方法内部以方法名和参数发起了HTTP请求,从请求的结果中获取到方法的执行结果
客户端调用方法的时候,会去发送一次HTTP请求,将方法名称和参数传递到服务端,服务端处理好之后,将结果写回到客户端。
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)) {
//....
}
else if (methodName.equals("hashCode") && params.length == 0)
//....
}
InputStream is = null;
HessianConnection conn = null;
try {
if (log.isLoggable(Level.FINER))
log.finer("Hessian[" + _url + "] calling " + mangleName);
//这里以方法名称和方法参数,发起一个HTTP请求
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 {
//....
}
}
总结
Hessian的客户端的每一次的方法调用实际上都是一次HTTP请求。具体来说,客户端每次调用代理对象的方法实际上是以方法名和参数发起HTTP请求到服务端,服务端接受到参数处理后,将结果写回到客户端的过程,客户端从流中获得处理结果。
参考博客: https://blog.csdn.net/sunwei_pyw/article/details/74002351