本文為原創,歡迎轉載,轉載請注明出處BlogJava。
在上一篇 Hessian構建分布式系統應用 的基礎上,我們對程序進行改進。
現在有以下比較突出的問題:
a.如果hessian服務端我要做的業務很多,怎么辦?
我要定義很多個接口,然后再寫實現類,最煩的是還要配置它。
我的設想是,hessian服務只提供一個歸口,再此對外的接口實現中反射調用具體的業務類。
b.客戶端在調用時,每次調用遠程接口都要用以下代碼嗎:
String url = "http://localhost:8080/HessianService/remote/service";
HessianProxyFactory factory = new HessianProxyFactory();
ServiceRemote rmt = (ServiceRemote) factory.create(ServiceRemote.class, url);
顯然是不需要的。
我們可以通過加入緩存的方式對其進行改良,我們也可以通過Spring在客戶端管理它。
一、完善hessian服務端實現:
1.首先修改ServiceRemote接口:
package com.al;

import java.util.Map;

@SuppressWarnings("unchecked")

public interface ServiceRemote
{
public Map callService(String target, Map inputMap) throws Exception;
}
callService為統一入口,在此做如下約定:
1)target字符串為要調用的service的完整類路徑+要調用的方法。
2)service的方法均用以下方法簽名:
public Map ***(Map inputMap);
入參為Map,返回值也為Map,基本可以滿足所有情況了。(至少入參為Map,很適合調用iBatis來對DB進行操作。)
2.修改接口實現類Service,此類不做具體業務,而是反射調用具體業務類:
package com.al;

import java.lang.reflect.Method;
import java.util.Map;

import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.lang.StringUtils;

@SuppressWarnings("unchecked")

public class Service implements ServiceRemote
{


public Map callService(String target, Map inputMap) throws Exception
{
String className = StringUtils.substringBeforeLast(target, ".");
String methodName = StringUtils.substringAfterLast(target, ".");
Class serviceClass = loadClass(className);
Method method = getMethod(serviceClass, methodName, Map.class);
// 提供訪問效率
method.setAccessible(true);
// 調用具體業務類
return (Map) method.invoke(serviceClass.newInstance(), inputMap);
}

private static <T> Class<T> loadClass(String className) throws ClassNotFoundException
{
return (Class<T>) getClassLoader().loadClass(className);
}

private static ClassLoader getClassLoader()
{
return Thread.currentThread().getContextClassLoader();
}

private static Method getMethod(Class<?> cls, String name, Class<?>
parameterTypes)
{
return MethodUtils.getAccessibleMethod(cls, name, parameterTypes);
}
}
3.舉個例子,服務端提供業務類DisplayUserService.java
package com.al.service;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("unchecked")

public class DisplayUserService
{
public static final String selectUsers = "com.al.service.DisplayUserService.selectUsers";
public static final String deleteUser = "com.al.service.DisplayUserService.deleteUser";

public Map selectUsers(Map inputMap)
{
Map ret = new HashMap();
// 數據庫操作取得用戶列表 省略
ret.put("User", "User");
return ret;
}

public Map deleteUser(Map inputMap)
{
// 數據庫操作取得用戶列表 省略
return null;
}
}
所有其他配置不變,請參考上一篇 Hessian構建分布式系統應用 。
二、客戶端代碼的修改:
1.加入spring進行管理:
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="serviceRemote" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8080/HessianService/remote/service" />
<property name="serviceInterface" value="com.al.ServiceRemote" />
</bean>
</beans>
2.客戶端如下調用即可:
package com.ai.client;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.al.ServiceRemote;
import com.al.service.DisplayUserService;


public class ClientTest
{

public static void main(String[] args) throws Exception
{
ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("application.xml");
ServiceRemote rmt = (ServiceRemote)cxt.getBean("serviceRemote");
System.out.println(rmt.callService(DisplayUserService.selectUsers, null));
}
}
另外一種方法是自己實現緩存。
也就是第一次調用遠程代碼時生成ServiceRemote對象,將其保存在靜態的容器(HashMap)中,
每次準備調用此遠程代碼時,先判斷容器中是否有ServiceRemote對象,有則直接將其取出并使用即可,要注意的就是在這個容器上的同步問題。
具體實現就不做了,很簡單。
在項目中,對于客戶端代碼來講,還是有許多工作要做的:
1) 如果我們要調用多個遠程服務怎么辦?
我們要提供一個統一調用,將遠程調用的動作封裝起來,讓使用的人不知道自己調用了不同的遠程服務。
只要調用某個方法、傳入參數即可。
2) 如何方便開發員調試遠程的服務代碼?
在做分布式系統開發的時候,如果每修改一下應用層的service,就要對其進行發布,然后再去調用看是否已OK,那效率會很低。
3) 如何管理多方調用的遠程服務?
4) 如何提高遠程調用的效率?
是否可以通過對 對象進行緩存、方法是否也可以緩存?甚至是對調用結果進行緩存?
5) 等等..
這些在具體的項目中都是不得不考慮的問題。以后再慢慢討論吧。