7.RPC框架的简单实现(服务引用-ReferenceBean实现与RMI协议)

上一篇rmi协议的服务发布已经完成,现在看一下服务引用的实现。ReferenceBean的实现同第四篇中的ServiceBean是不同的,ServiceBean是通过实现spring的InitializingBean接口,在afterPropertiesSet()方法中发布服务的。在spring容器中,ServiceBean初始化之后,就发布服务了,这个bean对象是没有其它对象引用它的。
对ReferenceBean来说,我们在xml配置的这个bean在初始化后,业务bean会引用这个bean的,通过ldubbo:reference的id我们可以得到这个bean,并且这个bean可以转为对应接口类。

在写ReferenceBean类的实现前,先创建一个consumer项目,跟上一篇的pom文件一样,引入ldubbo, ldubbo-api项目和slf4j的依赖,然后创建applicationContext-ldubbo.xml配置文件。

客户端最初约定的配置第二篇有提到,现在看一下客户端的spring配置文件applicationContext-ldubbo.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:ldubbo="http://www.lipenglong.com/schema/ldubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.lipenglong.com/schema/ldubbo
       http://www.lipenglong.com/schema/ldubbo/ldubbo.xsd" default-lazy-init="true">

    <ldubbo:registry address="127.0.0.1:1099" protocol="rmi"/>
    <ldubbo:reference interface="com.lipenglong.ldubbo.api.service.UserService" id="userService"/>

</beans>

配置文件定义好了,接着看一下consumer端我们怎么像dubbo那样调用服务,

package com.lipenglong.ldubbo.consumer;

import com.lipenglong.ldubbo.api.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * com.lipenglong.ldubbo.consumer
 * </p>
 * Created by lipenglong on 2017/8/30.
 */
public class Consumer {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-ldubbo.xml");
        UserService userService = (UserService) context.getBean("userService");
        System.out.println(userService);
        System.out.println(userService.queryUserList());
        System.out.println(userService.getUserById(23L));

    }
}

从spring的context中得到“userService”对象,转换为UserService 类,然后我们就可以调用它的方法了。

这里有没有这样的疑问,<ldubbo:reference /> 这个配置初始化后,根据id得到bean应该是ReferenceBean啊,在第3篇中LdubboNamespaceHandler的代码中

registerBeanDefinitionParser("reference", new LdubboBeanDefinitionParser(ReferenceBean.class));

reference配置应该转化为ReferenceBean对象,它怎么转换为UserService对象呢?

这里就要用到spring的FactoryBean接口,实现该接口的类,需要实现getObject(),getObjectType()和isSingleton()三个方法,根据该bean的id从spring容器中得到的不是FactoryBean本身,而是getObject()返回的对象。ReferenceBean实现了FactoryBean接口,所以当我们context.getBean("userService")时,得到并不是ReferenceBean对象,而是getObject()返回的对象,看一下ReferenceBean代码:

package com.lipenglong.ldubbo.config.spring;

import com.lipenglong.ldubbo.config.ReferenceConfig;
import org.springframework.beans.factory.FactoryBean;

/**
 * com.lipenglong.ldubbo.config.spring.ReferenceBean
 * </p>
 * Created by lipenglong on 2017/8/30.
 */
public class ReferenceBean extends ReferenceConfig implements FactoryBean {

    private static final long serialVersionUID = -2278602690695158523L;

    @Override
    public Object getObject() throws Exception {
        return get();
    }

    @Override
    public Class<?> getObjectType() {
        return getInterfaceClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

返回对象的get()方法在ReferenceConfig中实现,看一下ReferenceConfig的实现:

package com.lipenglong.ldubbo.config;

import com.lipenglong.ldubbo.rpc.Protocol;
import com.lipenglong.ldubbo.rpc.ProtocolFactory;

/**
 * com.lipenglong.ldubbo.config.ReferenceConfig
 * </p>
 * Created by lipenglong on 2017/8/30.
 */
public class ReferenceConfig<T> extends AbstractConfig {
    private static final long serialVersionUID = -226929659993307561L;
    private String interfaceName;
    private RegistryConfig registryConfig;
    private Class<?> interfaceClass;
    private Protocol protocol;
    private transient volatile T ref;

    public String getInterface() {
        return interfaceName;
    }

    public void setInterface(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public RegistryConfig getRegistryConfig() {
        return registryConfig;
    }

    public void setRegistryConfig(RegistryConfig registryConfig) {
        this.registryConfig = registryConfig;
    }

    public T get() {
        if (ref == null) {
            init();
        }
        return ref;
    }

    private void init() {
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        ref = createProxy();
    }

    private T createProxy() {
        protocol = ProtocolFactory.getProtocol(registryConfig.getProtocol());
        return (T) protocol.refer(interfaceClass, registryConfig);
    }

    public Class<?> getInterfaceClass() {
        if (interfaceClass != null) {
            return interfaceClass;
        }
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return interfaceClass;
    }
}

通过setInterface方法,把xml文件中配置的interface属性值赋值给interfaceName,setRegistryConfig方法,把xml配置中的RegistryConfig对象引用过来,因为要创建的interface属性配置的接口的代理类,代理类需要通过address和协议调用远程服务。

get()方法返回了一个泛型类(因为不知道具体的类型),当ref对象不存在时,执行init()方法。init方法中根据interfaceName得到interfaceClass,通过createProxy()方法得到ref对象。createProxy方法中根据registryConfig.getProtocol()获取xml中配置的具体协议(rmi或ldubbo),然后通过ProtocolFactory.getProtocol()方法根据配置的name得到具体的protocol实现类,调用protocol的refer方法得到代理类。

在上一篇rmi协议服务发布中有AbstractProxyProtocol的代码, AbstractProxyProtocol 类的refer方法,调用了抽象方法doRefer,RmiProtocol类的doRefer当时没有实现,现在看一下rmi协议类doRefer方法的具体实现:

@Override
    protected <T> T doRefer(Class interfaceClass, RegistryConfig registryConfig) {
        RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
        rmiProxyFactoryBean.setServiceUrl("rmi://" + registryConfig.getAddress() +
                "/" + interfaceClass.getName());
        rmiProxyFactoryBean.setServiceInterface(interfaceClass);
        rmiProxyFactoryBean.setCacheStub(true);
        rmiProxyFactoryBean.setLookupStubOnStartup(true);
        rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
        rmiProxyFactoryBean.afterPropertiesSet();
        return (T) rmiProxyFactoryBean.getObject();
    }

doRefer通过spring的RmiProxyFactoryBean类实现服务引用,new完对象后,设置url,接口和其它的一些配置,调用了afterPropertiesSet()方法,最后通过getObject()返回了代理类,所以RmiProxyFactoryBean同时实现了InitializingBean 和FactoryBean,看一下spring的源码,RmiProxyFactoryBean是在afterPropertiesSet()方法中创建代理类的。

RmiProtocol类通过spring rmi发布服务,得到的代理类,ldubbo这里只是把spring的rmi包装了一下,大家看一下spring rmi的相关文档,就发现服务端、客户端的实现就是我们的doExport()和doRefer()方法做的事情。

启动Provider端,然后运行Consumer,输出:
这里写图片描述

ldubbo框架客户端可以调用远程服务了!
rmi协议都是使用spring rmi的实现,我们可以不定义自己的<ldubbo:service/><ldubbo:reference/>等标签,直接像spring帮助文档中那样定义相关的<bean/>就可以实现,ldubbo这里加入rmi就是先理解RPC的服务发布和服务引用的过程,接下来就是ldubbo框架的核心部分了ldubbo协议的服务发布和引用过程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值