Spring目前提供了对RMI、 HttpInvoker、Hessian、Burlap及WebService等Remoting技术的集成。Spring屏蔽了这些实现技术的差异,用 户只需开发简单的Java对象(Plain Old Java Objects,POJO)然后按照Spring规定的格式进行配置文件的编写即可。
6.2.1 Hessian使用演示
【例6.1】在Spring中使用Hessian Remoting技术。
下面就来演示一下在Spring中是如何使用 Hessian Remoting技术的。Hessian、Burlap、HttpInvoker等是要运行在支持Servlet的Web服务器中的,因此在运行例子之前 要安装配置好Web服务器。Web服务器配置完毕以后按照下面的步骤编写代码。
(1) 编写业务接口:
// IWordProcessor业务接口
public interface IWordProcessor
{
/**
* 抽取value中的中文
* @param value
* @return
*/
public String extractChinese(String value);
}
(2) 编写实现类:
// 实现类
public class WordProcessorImpl implements IWordProcessor
{
public String extractChinese(String value)
{
Pattern p = Pattern.compile("[//u4E00-//u9FFF]+");
Matcher matcher = p.matcher(value);
StringBuffer sb = new StringBuffer();
while (matcher.find())
{
sb.append(matcher.group());
}
return sb.toString();
}
}
(3) 修改Web工程中的web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- 通过Spring的一个Servlet来完成对Hessian的代理 -->
<servlet>
<servlet-name>remote</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet<
/servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remote</servlet-name>
<url-pattern>/remote/*</url-pattern>
</servlet-mapping>
</web-app>
(4) 在Web工程中添加remote-servlet.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 name="/wordProcessorBean"
class="com.cownew.Char11.Sec02.WordProcessorImpl">
</bean>
<bean name="/WordProcessorService"
class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service">
<ref bean="wordProcessorBean" />
</property>
<property name="serviceInterface">
<value>com.cownew.Char11.Sec02.IWordProcessor</value>
</property>
</bean>
</beans>
(5) 编写客户端测试代码:
// 测试代码
package com.cownew.Char11.Sec02;
import java.net.MalformedURLException;
import com.caucho.hessian.client.HessianProxyFactory;
public class MainApp
{
public static void main(String[] args)
{
HessianProxyFactory proxyFactory = new HessianProxyFactory();
try
{
IWordProcessor service = (IWordProcessor) proxyFactory.create(
IWordProcessor.class, "http://localhost:8080/
RemoteCall/remote/WordProcessorService");
System.out.println(
service.extractChinese("人来的不少,I'm very 欣慰"));
} catch (MalformedURLException e)
{
e.printStackTrace();
}
}
}
运行结果:
人来的不少欣慰
用Web服务器来实现Remoting,确实很神奇!
如果需要改用Burlap,则将上面的 HessianServiceExporter改成BurlapServiceExporter,HessianProxyFactory改成 BurlapProxyFactory就可以,接口和实现类的代码均不需要修改;同样如果要改用HttpInoker,只要将上面的 HessianServiceExporter改成HttpInvokerService- Exporter,将HessianProxyFactory改成HttpInvokerProxyFactoryBean就可以了。
在案例系统开发的最初阶段曾经使用Hessian实 现Remoting,后来逐渐发现Hessian不能传递复杂对象的缺点,因此决定切换到Http Invoker,没想到从看资料到最终修改完毕竟然用了不到1分钟时间,其他部分完全不用修改,不得不为Spring折服。
6.2.2 几种Remoting实现的比较
Spring支持的Remoting实现技术是非常多的,虽然Spring屏蔽了这些技术使用上的差异,但是选择一个合适的Remoting技术仍然对系统有非常积极的作用,下面就来讲述这些实现技术的优缺点。
(1) RMI:RMI使用Java的序列化机制实现调用及返回值的编组(marshal)与反编组(unmarshal),可以使用任何可序列化的对象作为参数和返回值。其缺点是RMI只能通过RMI协议来进行访问,无法通过HTTP协议访问,无法穿透防火墙。
(2) Hessian:Hessian也是将网络传输的对象转换为二进制流通过Http进行传递,不过它是使用自己的序列化机制实现的编组与反编组,其支持的数据类型是有限制的,不支持复杂的对象。Hessian的优点是可以透过防火墙。
(3) Burlap:Burlap是将网络传输的对象转换为XML文本格式通过Http进行传递,支持的对象与Hessian相比更少。XML一般比二进制流占 用空间大,在网络上传递所需要的时间比二进制流长,XML的解析过程也会耗用更多的内存。Burlap可以穿透防火墙,而且由于传输的格式是XML文本, 可以与其他系统(比如.NET)集成,从某种程度来讲,Burlap是一种不标准的WebService。
(4) HttpInvoker:HttpInvoker将参数和返回值通过Java的序列化机制进行编组和反编组,它具有RMI的支持所有可序列化对象的优点。 Http Invoker是使用Http协议传输二进制流的,而同时又具有Hessian、Burlap的优点。
经过比较,并结合案例系统的特点,HttpInvoker在众多实现技术中脱颖而出,因此案例系统的Remoting部分将使用HttpInvoker实现。
6.3 改造HttpInvoker
HttpInvoker提供了 HessianServlet和HessianServiceExporter两种发布服务的方式,HessianServiceExporter比 HessianServlet简单一些,只要配置一个Spring IoC风格的配置文件即可:
<beans>
<bean name="/wordProcessorBean" class="com.cownew.Char11.Sec02.WordProcessorImpl">
</bean>
<bean name="/WordProcessorService" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service">
<ref bean="wordProcessorBean" />
</property>
<property name="serviceInterface">
<value>com.cownew.Char11.Sec02.IWordProcessor</value>
</property>
</bean>
</beans>
这是Spring官方文档中提到的使用方法,可是这 是最好的使用方法吗?想一想我们配置这个文件无非是要告诉容器3件事:向外提供的服务名字叫做WordProcessorService;服务实现了接口 com.cownew.Char11.Sec02.IWordProcessor;服务的实现类是com.cownew. Char11.Sec02.WordProcessorImpl。为了实现这3件事竟然要去写13行配置文件,如果要给服务对象添加AOP代理,那么还要 再添加一个<bean>…</bean>标记;如果要对外暴露100个服务,就要去写(13+n)*100行配置文件!
长篇大论、 没完没了的配置文件不是Spring的本意,把本应该写在代码里的依赖关系写到配置文件中是对Spring的最大滥用,甚至Rod Johnson本人也犯这样的错误。Java是强类型语言,这也是为什么Java成为工业级语言的重要原因,强类型可以尽早发现代码的错误,借助IDE使 用强类型可以提高开发效率。通过使用配置文件,可以将组件之间的依赖延迟到运行阶段,但是并不是任何依赖都需要延迟到运行阶段的。把本应该在开发阶段就组 装完毕且在运行时不会轻易改变的依赖放到配置文件中,不仅会导致代码难读、编写困难,也会降低系统的运行效率。
初学Spring的人往往喜欢把本来能在代码里完成的功能都改成配置文件方式,甚至personInfo.setParent(new PersonInfo(“Smith”))这样的代码也要配置到XML文件中。因为这样看起来很酷,因为把代码写到了配置文件中,这样会与众不同!但是软件开发不是玩玩具,“酷”不是选择一个技术的理由,这个技术必须解决实际问题才可以。
Spring 只是提供了一个解决问题的思路,Spring的IOC思想是非常简单易懂的,一个熟练的开发人员可以在很短的时间内重写Spring的核心。但是 Spring能够发展至今,靠的不是这个核心!试想如果没有Spring MVC、Spring AOP、Spring Remoting,没有Spring ORM,没有Spring JMS,我们还会如此痴迷Spring吗?
不要滥用Spring配置文件,不要把本应该在代码中注入的依赖放到配置文件中去,Spring的本意是简化,而不是复杂化!
那么下面看一下我们想要的配置文件是什么样的:
<bean id="WordProcessorService" class="com.cownew.Char11.Sec02.WordProcessorImpl" serviceInterface="com.cownew.Char11.Sec02.IWordProcessor">
</bean>
在这里指定了输出服务的标识为 “WordProcessorService”、实现该服务的类为 “com.cownew.Char11.Sec02.WordProcessorImpl”,该服务对应的接口为“com.cownew. Char11.Sec02.IWordProcessor”。
这个配置文件还可以进一步简化。真实的系统中会 存在大量的远程服务对象,如果每个对象都采取“WordProcessorService”这样的命名方式的话很容易重复,而且不容易管理。最好的命名方 式就是模仿Java的包机制,比如“com.cownew.Char11.Sec02.WordProcessorService”。既然此服务的调用者 知道此服务实现了“com.cownew.Char11.Sec02.IWordProcessor”接口,而且 “com.cownew.Char11.Sec02.IWordProcessor”这个名字不会重复,服务标识为什么不直接命名为 “com.cownew.Char11.Sec02.IWordProcessor”呢?这是个好注意!这样配置文件就可以被简化为:
<bean id="com.cownew.Char11.Sec02.IWordProcessor"
class="com.cownew.Char11.Sec02.WordProcessorImpl" >
</bean>
这就是我想要的!那么我们就来向着这个目标迈进吧。
HttpInvoker是如何在服务器端响应客 户端的调用请求,然后把调用的结果返回给客户端的呢?HttpInvoker与客户端交互的组件是DispatcherServlet,当Web服务器接 收到“http://localhost:8080/RemoteCall/remote”这个请求的时候就会将请求派发给 DispatcherServlet处理。通过阅读DispatcherServlet的代码可以得知,DispatcherServlet从请求中分辨 出客户端要调用的服务是“/WordProcessorService”,它就会到remote-servlet.xml中查找名称为“ /WordProcessorService”的Bean,最终查找到下面的配置文件声明了“/WordProcessorService”这个服务:
<bean name="/WordProcessorService"
class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service">
<ref bean="wordProcessorBean" />
</property>
<property name="serviceInterface">
<value>com.cownew.Char11.Sec02.IWordProcessor</value>
</property>
</bean>
DispatcherServlet调用IOC容器的方法得到这个服务。IOC容器发现这个Bean还引用了另外一个Bean:
<bean name="/wordProcessorBean"
class="com.cownew.Char11.Sec02.WordProcessorImpl">
</bean>
IOC容器首先实例化 “WordProcessorImpl”为名称为“wordProcessorBean”的Bean,然后实例化 “HessianServiceExporter”,设置“service”属性为“wordProcessorBean”对象,设置 “serviceInterface”属性为“com.cownew.Char11.Sec02.IWordProcessor”。IOC容器将实例化完 毕的“/WordProcessorService”对象返回给DispatcherServlet,DispatcherServlet把Web请求再 次派发给“/WordProcessorService”。
那么“/WordProcessorService”(即HttpInvokerServiceExporter类的对象)是如何响应Web请求的呢?打开HttpInvokerServiceExporter的源码,查看其实现代码,下面的公共方法引起我们的注意:
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
Assert.notNull(this.proxy,
"HttpInvokerServiceExporter has not been initialized");
try
{
RemoteInvocation invocation = readRemoteInvocation(request);
RemoteInvocationResult result = invokeAndCreateResult(invocation,
this.proxy);
writeRemoteInvocationResult(request, response, result);
}
catch (ClassNotFoundException ex)
{
throw new NestedServletException("Class not found during
deserialization", ex);
}
}
方法的名字和其参数以及方法内部的实现都暗示了它就是响应Web请求的核心方法,看到它的JavaDoc就更加表明我们的猜测是完全正确的:
Read a remote invocation from the request, execute it, and write the remote invocation result to the response.(从请求中读取远程调用,执行调用,然后将调用结果写到响应中去。)
handleRequest方法是 HttpRequestHandler接口中定义的响应Http请求的接口,BurlapServiceExporter、 HessianServiceExporter、HttpInvokerServiceExporter都实现了这个接口。request是客户端的请 求,客户端的方法调用全部在request中;response是返回给客户端的响应对象,我们要把调用结果(包括返回值、异常等)通过response 返回给客户端。
弄懂了HttpInvokerServiceExporter的实现原理,下面就来实现要解析的配置文件:
<bean id="com.cownew.Char11.Sec02.IWordProcessor"
class="com.cownew.Char11.Sec02.WordProcessorImpl" >
</bean>
由于上面的这个配置文件格式是自定义的,DispatcherServlet和HttpInvokerServiceExporter都无法识别它,必须写一个Servlet来处理调用请求。
【例6.2】提供Remoting服务的Servlet。
编写一个从HttpServlet继承的RemotingCallServlet:
// 提供Remoting服务的Servlet
public class RemotingCallServlet extends HttpServlet
{
private static Logger logger = Logger.getLogger(RemotingCallServlet.class);
private static BeanFactory appContext = null;
protected void doPost(HttpServletRequest httpRequest,
HttpServletResponse httpResponse) throws ServletException,
IOException
{
try
{
invokeService(httpResponse, httpRequest);
}catch (Throwable e)
{
// 注意,bean运行过程中的异常并不是通过此处抛出的
//而是通过remoting机制传递到客户端再抛出的
// 此处抛出的是非bean的异常
//由于这里的异常不会抛出到客户端,因此把异常打印出来,方便开发调试
//使用log4j把异常打印出来是一种好习惯!
logger.error(e.getMessage(), e);
throw new ServletException(e);
}
}
private void invokeService(HttpServletResponse response,
HttpServletRequest request) throws ServletException, PISException
{
String reqPath = request.getPathInfo();
String serviceId = getServiceId(reqPath);
invokeBean(request, response, serviceId);
}
private void invokeBean(HttpServletRequest request,
HttpServletResponse response, String serviceId)
throws ServletException, PISException
{
Object _service = appContext.getBean(serviceId);
//因为所有的服务都是无状态的服务,所以此处的_service无须进行同步,
//可以同时为多个调用服务
try
{
HttpInvokerServiceExporter exporter =
new HttpInvokerServiceExporter();
exporter.setService(_service);
exporter.setServiceInterface(Class.forName(serviceId));
exporter.afterPropertiesSet();
exporter.handleRequest(request, response);
} catch (ClassNotFoundException e)
{
throw new ServletException(e);
} catch (IOException e)
{
throw new ServletException(e);
}
}
//用正则表达式将Path中的服务id提取出来,比如“/com.cownew.demo.IService”
//将“com.cownew.demo.IService”解析出来
private static String getServiceId(String reqPath)
{
Pattern pattern = Pattern.compile("/(.+)");
Matcher match = pattern.matcher(reqPath);
match.matches();
match.group();
String serviceId = match.group(1);
return serviceId;
}
static
{
appContext = new ClassPathXmlApplicationContext(
"com/cownew/PIS/framework/server/springBeans.xml");
}
}
编写配置文件springBeans.xml放到和RemotingCallServlet同一级的包下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "file:///E:/保留文档/java/常用包/spring/spring-framework-1.2.8/dist/spring-beans.dtd">
<beans>
<bean id="com.cownew.Char11.Sec02.IWordProcessor"
class="com.cownew.Char11.Sec02.WordProcessorImpl" >
</bean>
</beans>
修改Web工程中的web.xml文件,将DispatcherServlet替换成RemotingCallServlet,删除remote-servlet.xml文件,然后重启服务器。
编写测试客户端:
public class MainApp
{
public static void main(String[] args)
{
HttpInvokerProxyFactoryBean proxyFactory =
new HttpInvokerProxyFactoryBean();
try
{
proxyFactory.setServiceUrl(
"http://localhost:8080/RemoteCall/remote/"
+IWordProcessor.class.getName());
proxyFactory.setServiceInterface(IWordProcessor.class);
proxyFactory.setHttpInvokerRequestExecutor(new
CommonsHttpInvokerRequestExecutor());
proxyFactory.afterPropertiesSet();
IWordProcessor service = (IWordProcessor)proxyFactory.getObject();
System.out.println(service.extractChinese(
"人来的不少,I'm very 欣慰"));
} catch (MalformedURLException e)
{
e.printStackTrace();
}
}
}
运行结果:
人来的不少欣慰
RemotingCallServlet的核心 代码在invokeBean中。首先使用Spring的ClassPathXml- ApplicationContext的getBean方法得到服务,然后实例化HttpInvokerServiceExporter,把通过 getBean方法得到的服务Bean对象赋值给setService方法。这里规定服务的id和服务的接口类名一致,所以调用 Class.forName(serviceId)即可反射得到接口类名,把类名赋值给setServiceInterface方法。
在例子中通过Spring的配置文件来为 HttpInvokerServiceExporter设置service、serviceInterface属性,而此处是直接在代码中完成的注入。 Spring中大部分类都实现了InitializingBean接口,Spring在为Bean设置完属性后会调用InitializingBean接 口的afterPropertiesSet方法来标识属性设置完毕,实现类常常在afterPropertiesSet方法中做属性合法性检验、数据初始 化等操作,因此在这种代码注入的情况下要手动调用afterPropertiesSet方法,以防出错。代码最后调用了handleRequest来响应 客户端请求。
不按照Spring推荐的配置文件的方式来使用 Spring的类初看好像是对Spring的错误使用,实则是一种最佳的使用方式。此处由于服务接口和实现的动态性,在Spring中用配置文件实现起来 是非常困难的,即使能够实现配置文件看起来也是非常难懂的,而通过这种代码注入的方式看起来却更简单明了。在使用Spring的时候,使用配置文件注入一 定要有充分的理由,不能人云亦云。
客户端测试代码中 HttpInvokerProxyFactoryBean的初始化方式也是从Spring的HttpInvoker使用手册的XML文件配置方式翻译过来 的,此处由于只是得到IWordProcessor服务,我们完全可以按照配置文件的方式进行注入,但是我们后边将会将这种调用方式封装成一个能承担各种 服务调用工作的RemoteServiceLocator,所以此处仍然使用代码方式进行注入。案例系统中使用Spring的配置文件方式注入的地方是非 常少的,所以在后边的代码分析中再见到类似的“反Spring”的使用方式的时候就无须大惊小怪了。
经过上边的改造,实现一个新的服务所需要的工作已经 减少很多了,只需完成下面的工作就可以实现一个Remoting服务:编写服务接口;编写实现类;在springBeans.xml加入<bean id="服务接口类名" class="服务实现类名" ></bean>。
这个框架还有以下两点可以进一步改进的:
l 服务文件的分模块化。每增加一个服务都要向springBeans.xml中加入一个服务的定义,如果整个系统的服务都定义在这一个文件中,当多人甚至多 项目组协同开发的时候这个文件的修改将成为一个灾难,会经常出现多个人同时修改这一个文件造成冲突的问题。要对此处做改进,使得可以支持多 springBeans.xml文件,每个项目组都定义自己的springBeans.xml文件。
l 抽象出本地服务加载器。在invokeBean方法中使用appContext.getBean(serviceId)方法来取得本地服务Bean,这暗 示了系统是使用Spring IOC来管理服务的,但这个事实是无须RemotingCallServlet 知道的,RemotingCallServlet 只是想通过serviceId来得到服务实现,至于服务的加载方式RemotingCallServlet 无须关心。再者在服务器端,各个模块之间也要相互协作,模块之间无须知道具体的实现类是什么而是通过接口直接调用的。如果调用其他模块的时候都要使用 appContext.getBean(serviceId)来加载服务的话,这无疑使得Spring IOC的使用蔓延到了整个系统。基于以上两点考虑,系统需要一个本地服务加载器。
6.3.1 服务文件的分模块化
由于每个模块都定义一个springBeans.xml文件,所以系统内部的各个包中会散布着这些文件,要通过它们加载服务的话,必须首先加载它们,要加载它们就首先要知道它们的位置。得到所有springBeans.xml文件有两种实现方式。
(1) 遍历系统每个包,发现名字为springBeans.xml的配置文件就加载进来;
(2) 在系统的一个配置文件中保存这些springBeans.xml文件的位置,只要读取这个配置文件就可以知道所有springBeans.xml文件的位置了。
第一种方式简单灵活,开发人员可以在任意位置编写 springBeans.xml文件,系统都可以加载到它们。缺点就是遍历所有的包需要一定的时间,会降低系统的初始化速度;文件名只能为 springBeans.xml,而且系统中不能有用作其他用途的名称为“springBeans.xml”的文件。
第二种方式比较严谨,各个模块必须严格遵守在配置文件中声明的位置建立springBeans.xml文件,文件的名字也可以改变成其他的;配置文件的加载速度也会有所提高,从而加快系统的初始化速度;缺点就是每个模块都必须到统一的配置文件中注册,加大了工作量。
在大型的团队开发中,第二种方式比第一种方式拥有更多的优势,所以此处按照第二种思路实现。
在包“/com/cownew/PIS/framework/server/”下建立文件ServerConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Config>
<!--remoting文件的位置-->
<BeanFiles>
<File>/com/cownew/PIS/framework/server/springBeans.xml</File>
<File>/com/cownew/PIS/framework/server/springBeans2.xml</File>
</BeanFiles>
</Config>
在BeanFiles标记中定义的就是所有的springBeans.xml文件。
【例6.3】建立一个配置文件读取类。
为了读取这个文件,下面建立一个应用服务器端配置文件读取类:
// 应用服务器端配置文件读取器
package com.cownew.PIS.framework.server.helper;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.dom4j.tree.DefaultElement;
import com.cownew.ctk.common.ExceptionUtils;
import com.cownew.ctk.common.StringUtils;
import com.cownew.ctk.constant.StringConst;
import com.cownew.ctk.io.ResourceUtils;
public class ServerConfig
{
private String[] beanFiles;
private static ServerConfig instance = null;
private ServerConfig()
{
super();
};
public static ServerConfig getInstance()
{
if (instance == null)
{
instance = new ServerConfig();
try
{
instance.initConfig();
} catch (Exception e)
{
ExceptionUtils.toRuntimeException(e);
}
}
return instance;
}
protected void initConfig() throws Exception
{
InputStream beansXFStream = null;
try
{
beansXFStream = getClass().getResourceAsStream(
"/com/cownew/PIS/framework/server/ServerConfig.xml");
SAXReader reader = new SAXReader();
reader.setValidation(false);
Document doc = reader.read(new InputStreamReader(beansXFStream,
StringConst.UTF8));
loadBeanFilesDef(doc);
} finally
{
ResourceUtils.close(beansXFStream);
}
}
/**
* Remoting定义文件
*/
public String[] getBeanFiles()
{
return beanFiles;
}
/**
* 加载remoting配置文件
*/
private void loadBeanFilesDef(Document doc)
{
List beanList = doc.selectNodes("//Config/BeanFiles/File");
beanFiles = new String[beanList.size()];
for (int i = 0, n = beanList.size(); i < n; i++)
{
DefaultElement beanElement = (DefaultElement) beanList.get(i);
beanFiles[i] = beanElement.getText();
}
}
}
配置文件的解析是一个比较费时的过程,所以在这里只 是在ServerConfig实例化的时候调用initConfig进行配置文件的解析,并把解析后的结果保存在beanFiles数组中。为了防止调用 者实例化此读取类,所以将此类设计成单例的,并且实现为惰性加载,通过getInstance返回这个单例。这样通过 ServerConfig.getInstance().getBeanFiles()就可以得到所有配置文件的位置了。
在这里规定在ServerConfig.xml定义的配置文件位置必须是以类路径形式表示的,ClassPathXmlApplicationContext有一个支持字符串数组的构造函数,所以只要修改Bean工厂的实例化方式为:
appContext = new ClassPathXmlApplicationContext(ServerConfig
.getInstance().getBeanFiles());
就可以一次性加载所有的配置文件了。
6.3.2 本地服务加载器
目前阶段的本地服务加载器的实现是非常简单的,只要在适当的时候创建Bean工厂,并调用appContext的相应方法来取得相应的服务对象即可。
【例6.4】本地服务加载器。
代码如下:
// 本地服务加载器
public class LocalServiceLocator
{
private static LocalServiceLocator instance;
private LocalServiceLocator()
{
super();
};
private static BeanFactory appContext = null;
public static LocalServiceLocator getInstance()
{
if (instance == null)
{
instance = new LocalServiceLocator();
}
return instance;
}
public Object getService(Class serviceIntfClass) throws PISException
{
String serviceId = serviceIntfClass.getName();
Object bean = appContext.getBean(serviceId);
return bean;
}
static
{
appContext = new ClassPathXmlApplicationContext(ServerConfig
.getInstance().getBeanFiles());
}
}
将RemotingCallServlet的invokeBean方法中根据serviceId得到服务的代码替换为下面的方式:
Class serviceIntfClass = Class.forName(serviceId);
Object _service = LocalServiceLocator.getInstance()
.getService(serviceIntfClass);