服务的运行期动态注册 <o:p></o:p>
服务的自动注入( IoC ) <o:p></o:p>
透明化服务 ID 的传递 <o:p></o:p>
<o:p> </o:p>
在服务池( ServicePool )概念的基础上进行扩展,我们得出了如下的系统模型:
![]() |
在核心位置上是一个服务中心资源库( ServiceRepository ),存储了系统中用到的所有的远程服务。服务采取动态注册的机制,由对外提供的服务注册器( ServiceRegister )提供服务注册功能。外部系统可以实现该接口向资源中心注册服务。提供了一个启动时运行的注册器,可以把静态配置在系统中的服务都注册进来。 <o:p></o:p>
<o:p> </o:p>
服务的生成、管理等均由服务中心自己维护,委托服务代理生成器( ServiceProxyGenerator )完成服务的创建。可以针对现有的远程调用方式,如 XFire,HttpInvoker,Hessian 等创建服务代理,也可以针对自己定义的远程调用方式创建服务代理,由 CustomServiceProxyGenerator 完成该功能。 <o:p></o:p>
一个服务模型包括 5 个因素: <o:p></o:p>
服务接口 serviceClass<o:p></o:p>
服务 ID serviceId<o:p></o:p>
服务类型 serviceType<o:p></o:p>
服务地址 serviceUrl<o:p></o:p>
附加属性 props<o:p></o:p>
查找一个服务需要两个因素,一个是服务接口,另一个是服务 ID 。这两个因素共同决定了一个服务,既服务中心内部的“服务 ID ”。通过这种方式,可以允许存在多个 ID 相同但接口不同的服务,也可以存在多个接口相同但 ID 不同的服务。 <o:p></o:p>
<o:p> </o:p>
服务 ID 的获取是系统中一个关键的功能,这部分对程序员来说应该是透明的,由系统自己维护。相应的提供了一个服务 ID 提供者 (ServiceIdProvider) 接口,由实现该接口的子类完成服务 ID 获取功能(这是比较关键的地方,需要特殊考虑)。 <o:p></o:p>
<o:p> </o:p>
对于程序员来说,使用服务中心里的服务再也不能比这样再简单了!看看配置:
- < bean id = "helloHttpInvokerService" parent = "abstractServiceProxyFactory" >
- < property name = "serviceInterface" >
- < value > com.tonysoft.common.service.repository.example.HelloHttpInvoker value >
- property >
- bean >
再看如何使用这个 bean :
- private HelloHttpInvoker helloHttpInvokerService ;
- public void testHttpInvoker() {
- assertNotNull( "helloHttpInvokerService can't be null !" , helloHttpInvokerService );
- assertEquals ( "Hello , HttpInvoker !" , helloHttpInvokerService .sayHello());
- }
- /**
- * @param helloHttpInvokerService
- * the helloHttpInvokerService to set
- */
- public void setHelloHttpInvokerService(HelloHttpInvoker helloHttpInvokerService) {
- this . helloHttpInvokerService = helloHttpInvokerService;
- }
就是这样的简单! Spring 会把这个 bean 自动注入到程序中,可以象使用其他任何 bean 一样使用它!程序员完全不用关心该服务由谁提供、采用什么技术,他只要知道系统中存在这样一个服务就 OK 了。该技术彻底向程序员屏蔽了底层技术的实现细节,以统一的方式访问任何形式的远程服务。至于服务是如何生成、如何配置的将在后面叙述。 <o:p></o:p>
<o:p> </o:p>
服务( Service Bean )是如何实现自动注入( IoC )的呢? <o:p></o:p>
注意到上面配置的 bean 都继承了“ abstractServiceProxyFactory ”,它是一个工厂 bean ,负责根据给定的接口类型,到服务中心( ServiceRepository )查找服务,并生成服务代理。我们来看一下它的核心代码:
- /**
- * 服务代理工厂。
- *
- *
- * 该工厂对程序员屏蔽了服务实现的技术细节,对于 XFire 、 Hessian 、 HttpInvoker 等常用远程服务形式进行封装。
- *
- *
- * 程序员只需要提供一个服务接口(契约),该工厂会从服务中心
ServiceRepository
中查找符合该接口的远程服务实例。 - *
- *
- * 查找的规则是由服务 ID 提供者所提供的服务 ID 和服务接口名字共同组成的服务关键字匹配。
- *
- * @author Tony
- */
- public class ServiceProxyFactory implements FactoryBean {
- /** 服务中心 */
- private ServiceRepository serviceRepository ;
- /** 服务 ID 提供者 */
- private ServiceIdProvider serviceIdProvider ;
- /** 服务接口 */
- private Class serviceInterface ;
- /**
- * @see org.springframework.beans.factory.FactoryBean#getObject()
- */
- public Object getObject() throws Exception {
- return ProxyFactory.getProxy(getObjectType(), new ServiceProxyInterceptor());
- // return serviceRepository.getService(serviceInterface, serviceIdProvider.getCurrentServiceId());
- }
- /**
- * @see org.springframework.beans.factory.FactoryBean#getObjectType()
- */
- public Class getObjectType() {
- return serviceInterface ;
- }
- /*
- * @see org.springframework.beans.factory.FactoryBean#isSingleton()
- */
- public boolean isSingleton() {
- return true ;
- }
- /*
- * 远程服务代理拦截器。
- */
- private class ServiceProxyInterceptor implements MethodInterceptor {
- /*
- * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
- */
- public Object invoke(MethodInvocation invocation) throws Throwable {
- Method method = invocation.getMethod();
- Object[] args = invocation.getArguments();
- Object client = getClient();
- return method.invoke(client, args);
- }
- private Object getClient() {
- try {
- return serviceRepository .getService( serviceInterface , serviceIdProvider .getCurrentServiceId());
- } catch (ServiceException e) {
- // TODO
- e.printStackTrace();
- return null ;
- }
- }
- }
- // ---- 容器自动注入 ----
- ······
真正的魅力就在这个地方。根据服务接口类型和服务 ID ,从服务中心获取特定的服务。服务接口是配置好的, 而服务 ID 则在运行时才能确定,根据不同的应用、不同的策略提供不同的 ServiceIdProvider 。其中用到了 Spring 的 FactoryBean 和拦截器,至于为什么要在这里使用拦截器,可以参考 Spring 框架的源码。 <o:p></o:p>
<o:p> </o:p>