现在的位置: 首页 > 综合 > 正文

在Spring中使用Hessian Remoting技术。

2013年09月15日 ⁄ 综合 ⁄ 共 18133字 ⁄ 字号 评论关闭
文章目录

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);

 

抱歉!评论已关闭.