3.RPC框架的简单实现(解析自定义bean)

xsd文件,spring.handlers和spring.schemas文件定义好了之后,接下来就是定义自己的NamespaceHandler处理类,如下:

package com.lipenglong.ldubbo.config.spring.schema;

import com.lipenglong.ldubbo.config.ProtocolConfig;
import com.lipenglong.ldubbo.config.RegistryConfig;
import com.lipenglong.ldubbo.config.spring.ReferenceBean;
import com.lipenglong.ldubbo.config.spring.ServiceBean;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * 命名空间Handler类,spring框架的调用入口
 * <p/>
 * Created by lipenglong on 2017/7/21.
 */
public class LdubboNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("protocol", new LdubboBeanDefinitionParser(ProtocolConfig.class));
        registerBeanDefinitionParser("service", new LdubboBeanDefinitionParser(ServiceBean.class));
        registerBeanDefinitionParser("registry", new LdubboBeanDefinitionParser(RegistryConfig.class));
        registerBeanDefinitionParser("reference", new LdubboBeanDefinitionParser(ReferenceBean.class));
    }
}

对ldubbo:protocol bean解析为ProtocolConfig对象,ldubbo:registry bean解析为RegistryConfig对象。ldubbo:service 和 ldubbo:reference的bean解析为了ServiceBean和ReferenceBean对象。
为什么不都是config对象?对protocol和registry来说就是配置的bean,是简单的Java pojo对象。对service和reference来说不仅是配置,还要暴露服务和引用服务,它们需要继承spring的一些工厂类,来实现bean初始化后的其它操作即服务的暴露和引用。

关于上面LdubboNamespaceHandler类,config 对象,bean对象跟dubbo源码中的结构是一致的,包路径也一样,ldubbo就是模仿dubbo来写的一个简单的rpc实现。接下来是LdubboBeanDefinitionParser类具体解析xml的实现,代码如下:

package com.lipenglong.ldubbo.config.spring.schema;

import com.lipenglong.ldubbo.config.ProtocolConfig;
import com.lipenglong.ldubbo.config.RegistryConfig;
import com.lipenglong.ldubbo.config.spring.ServiceBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import java.lang.reflect.Method;

/**
 * ldubbo bean xml配置文件解析类,将信息放入spring容器
 * <p/>
 * Created by lipenglong on 2017/7/21.
 */
public class LdubboBeanDefinitionParser implements BeanDefinitionParser {
    private final Class<?> beanClass;

    public LdubboBeanDefinitionParser(Class<?> serviceBeanClass) {
        this.beanClass = serviceBeanClass;
    }

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        //判断bean的id是否赋值
        String id = element.getAttribute("id");
        if (StringUtils.isEmpty(id)) {
            if (ServiceBean.class.equals(beanClass)) {
                id = element.getAttribute("interface");
            } else if (ProtocolConfig.class.equals(beanClass)) {
                id = element.getAttribute("name");
            } else {
                id = beanClass.getSimpleName();
            }
        }
        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
        beanDefinition.getPropertyValues().addPropertyValue("id", id);

        //遍历class的set方法,属性赋值
        for (Method method : beanClass.getMethods()) {
            String name = method.getName();
            if (name.length() > 3 && name.startsWith("set")) {
                String property = name.substring(3, 4).toLowerCase() + name.substring(4);
                String value = element.getAttribute(property);
                if (StringUtils.isEmpty(value)) {
                    if ("protocolConfig".equals(property)) {
                        for (String beanName : parserContext.getRegistry().getBeanDefinitionNames()) {
                            BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(beanName);
                            if (ProtocolConfig.class.getName().equals(definition.getBeanClassName())) {
                                beanDefinition.getPropertyValues().addPropertyValue(property, definition);
                            }
                        }
                    } else if ("registryConfig".equals(property)) {
                        for (String beanName : parserContext.getRegistry().getBeanDefinitionNames()) {
                            BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(beanName);
                            if (RegistryConfig.class.getName().equals(definition.getBeanClassName())) {
                                beanDefinition.getPropertyValues().addPropertyValue(property, definition);
                            }
                        }
                    }
                    continue;
                }
                Object reference;
                if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                    reference = new RuntimeBeanReference(value);
                } else {
                    reference = value;
                }
                beanDefinition.getPropertyValues().addPropertyValue(property, reference);
            }
        }
        return beanDefinition;
    }
}

parse方法把xml的配置解析为config对象,首先判断id是否赋值,xml没有配置id的话指定一个id。然后遍历class的set方法,得到属性名property,通过element.getAttribute(property)获取xml中配置的value,对于value不为空的,说明对象属性是xml的属性,如果属性是ref,那么说明值是引用的对象,并且spring上下文有这个bean时,把value设置为new RuntimeBeanReference(value)对象。

回看一下最初spring配置文件中约定的ldubbo配置:

<ldubbo:protocol name="ldubbo"/>
<ldubbo:service interface="com.lipenglong.ldubbo.api.service.UserService" ref="userService"/>

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

里面除了ldubbo:service ref对应的是引用对象外,其它都是String类型。

ProtocolConfig对象定义:

package com.lipenglong.ldubbo.config;

/**
 * protocol配置类
 * <p/>
 * Created by lipenglong on 2017/7/25.
 */
public class ProtocolConfig extends AbstractConfig {
    private static final long serialVersionUID = -5067525426300152084L;
    // 服务协议
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "ProtocolConfig{" +
                "name='" + name + '\'' +
                '}';
    }
}

RegistryConfig对象定义:

package com.lipenglong.ldubbo.config;

/**
 * registry配置类
 * </p>
 * Created by lipenglong on 2017/8/30.
 */
public class RegistryConfig extends AbstractConfig {
    private static final long serialVersionUID = -128869717961737215L;
    private String address;
    private String protocol;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public String getRegistryIp() {
        return address.substring(0, address.indexOf(":"));
    }

    public int getRegistryPort() {
        return Integer.parseInt(address.substring(address.indexOf(":") + 1));
    }

    @Override
    public String toString() {
        return "RegistryConfig{" +
                "address='" + address + '\'' +
                ", protocol='" + protocol + '\'' +
                '}';
    }
}

因为address属性在xml中填写的ip和端口,所以提供getRegistryIp()和getRegistryPort()两个方法分别获取ip地址和端口配置。

config对象类都继承了AbstractConfig类,这个是自己定义的父类,拥有id属性,AbstractConfig类定义如下:

package com.lipenglong.ldubbo.config;

import java.io.Serializable;

/**
 * ldubbo config配置父类
 * <p/>
 * Created by lipenglong on 2017/7/25.
 */
public abstract class AbstractConfig implements Serializable {
    private static final long serialVersionUID = 4408739851966423744L;
    protected String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

关于ServiceBean和ReferenceBean对象,分别继承ServiceConfig和ReferenceConfig,它们的定义跟上面的config对象类似,接下来我们先写暴露服务的ServiceBean类的具体实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值