Implement the export of the dubbo service by yourself

1. Today we are going to implement three functions of dubbo:

  1. Definition of dubbo tags
  2. The dubbo tag is parsed and assigned to the corresponding XXConfig (eg: ServiceConfig corresponds to the dubbo:service tag)
  3. How dubbo uses the life cycle of spring bean to export dubbo:service services

2. Code
implementation As we all know, spring provides a function that allows us to customize labels (don't understand, google it yourself), dubbo uses it like this:
create three files (file content, which will be downloaded later):
src/META -INF/dubbo.xsd
src/META-INF/spring.handlers
src/META-INF/spring.schemas

When spring initializes a custom label, it will call back the init method of NamespaceHandlerSupport

/***
 * spring 解析 <dubbo:> 标签执行
 * 
 * @author HadLuo
 * @since JDK1.7
 * @history 2018年5月2日 新建
 */
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        // <dubbo:application> 标签
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationBean.class));
         // <dubbo:registry> 标签
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryBean.class));
        // <dubbo:protocol> 标签
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolBean.class));
        // <dubbo:service> 标签
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class));
    }
}

DubboBeanDefinitionParser parser, spring will automatically call the parse method

public class DubboBeanDefinitionParser implements BeanDefinitionParser {

    private Class<?> beanClass;
    private static final DubboIdStrategy ID_STRATEGY = new DubboIdStrategy();

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

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 动态注入 spring bean
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        String id = element.getAttribute("id");
        if (id == null || id.isEmpty()) {
            // dubbo标签中没有id属性的 , 生成一个不重复的id
            id = IdGenerator.generateId(ID_STRATEGY, beanClass);
        }
        if (parserContext.getRegistry().containsBeanDefinition(id)) {
            // spring 有这个id 的bean  了 直接抛出异常
            throw new IllegalStateException("Duplicate spring bean id " + id);
        }
        // 将 dubbo标签bean 注入spring
        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);

        //  为 dubbo标签bean设置属性值 , 这里不同于dubbo源码(源码太繁琐了), 这里直接 用反射 设置基本类型值,利用holder来设置 属性为 dubbo bean对象 的值
        for (Field field : ReflectUtils.getAllFieldsByAnnotation(beanClass, Tag.class)) {
            String tag = field.getAnnotation(Tag.class).value();
            Class<?> holderClass = field.getAnnotation(Tag.class).holder();
            beanDefinition.getPropertyValues().addPropertyValue(field.getName(),
                    getValueByType(field, element.getAttribute(tag),holderClass));
        }
        return beanDefinition;
    }

    /***
     * 设置字段值
     * 
     * @param field
     * @param target
     * @param val
     * @throws NumberFormatException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @author HadLuo 2018年4月13日 新建
     */
    @SuppressWarnings("rawtypes")
    private static Object getValueByType(Field field, String val,Class<?> holderClass) {
        if (val == null) {
            return null;
        }
        if (field.getType().equals(Integer.class) || field.getType().equals(int.class)) {
            return Integer.parseInt(val);
        } else if (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class)) {
            return Boolean.parseBoolean(val);
        } else if (field.getType().equals(Byte.class) || field.getType().equals(byte.class)) {
            return Byte.parseByte(val);
        } else if (field.getType().equals(Character.class) || field.getType().equals(char.class)) {
            return val.charAt(0);
        } else if (field.getType().equals(Double.class) || field.getType().equals(double.class)) {
            return Double.parseDouble(val);
        } else if (field.getType().equals(Float.class) || field.getType().equals(float.class)) {
            return Float.parseFloat(val);
        } else if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
            return Long.parseLong(val);
        } else if (field.getType().equals(Short.class) || field.getType().equals(short.class)) {
            return Short.parseShort(val);
        } else if (field.getType().equals(String.class)) {
            return val;
        } else if (ReflectUtils.isChild(field.getType(), Holder.class)) {
            // 代表 是 对象 类型 , 从spring里面 找
            String id = ID_STRATEGY.getFixBeanId(holderClass);
            if (id == null || id.isEmpty()) {
                throw new UnsupportedOperationException(field.getType().getName() + " 不能赋值为 :" + val);
            }
            // 用holder来 占位 , 后面初始化时 ,直接 利用spring context根据id来找dubbo bean
            return new Holder(id);
        }
        throw new UnsupportedOperationException(field.getType().getName() + " 不能赋值为 :" + val);
    }
}

ServiceBean implements several life cycle methods of spring bean and inherits ServiceConfig (configuration value of service tag)

public class ServiceBean extends ServiceConfig implements SpringBeanLifecycle {
    // 是否已经导出 ServiceBean 服务
    private transient boolean exported;
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
            if (exported) {
                return;
            }
            setExported(true);
            export();
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.err.println("spring init " + ServiceBean.class.getName() + " bean");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        registerApplicationContext(applicationContext);
    }

    @Override
    public void setBeanName(String name) {
    }

    public synchronized void setExported(boolean exported) {
        this.exported = exported;
    }

    public synchronized boolean exported() {
        return exported;
    }

    @Override
    public void destroy() throws Exception {
        setApplicationContext(null);
        setExported(false);
    }
}

ServiceConfig configuration information class (including implementation exports).

public class ServiceConfig extends AbstractConfig {
    /** 服务接口全路径                Tag注解,是我自己实现的 不同于dubbo*/
    @Tag("interface")
    private String _interface;
    /** 服务对象实现引用 */
    @Tag("ref")
    private String ref;
    /** 远程服务调用超时时间(毫秒) */
    @Tag("timeout")
    private long timeout = 1000;
    /** 负载均衡策略,可选值为:random(随机)、roundrobin(轮询)、leastactive(最少活跃调用) */
    @Tag("loadbalance")
    private String loadbalance = "random";
    /** 是否缺省异步执行,不可靠的异步,只是忽略返回值,不阻塞执行线程 */
    @Tag("async")
    private boolean async = false;
    /***holder 注入, 复杂对象****/
    @Tag(holder = ApplicationBean.class)
    private Holder<ApplicationBean> applicationHolder;
    /***holder 注入, 复杂对象****/
    @Tag(holder = ProtocolBean.class)
    private Holder<ProtocolBean> protocolHolder;

    public String getInterface() {
        return _interface;
    }

    public String getRef() {
        return ref;
    }

    public long getTimeout() {
        return timeout;
    }

    public String getLoadbalance() {
        return loadbalance;
    }

    public boolean isAsync() {
        return async;
    }

    public void set_interface(String _interface) {
        this._interface = _interface;
    }

    public void setRef(String ref) {
        this.ref = ref;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    public void setAsync(boolean async) {
        this.async = async;
    }

    public void setApplicationHolder(Holder<ApplicationBean> applicationHolder) {
        this.applicationHolder = applicationHolder;
    }

    public void setProtocolHolder(Holder<ProtocolBean> protocolHolder) {
        this.protocolHolder = protocolHolder;
    }

    public void setLoadbalance(String loadbalance) {
        this.loadbalance = loadbalance;
    }

    // dubbo://10.112.6.12:20880/cn.javacoder.test.dubbo.IHelloWorldService?application=test-provider
    // &dubbo=2.5.3&interface=cn.javacoder.test.dubbo.IHelloWorldService&methods=say&pid=6816&side=provider&timestamp=1522284835101
    public void export() {
        if (getApplicationContext() == null) {
            throw new RuntimeException("spring 还没有初始化完成");
        }
        wakeupHolder();
        // 这里 模拟 ======================
        String url = "dubbo://" + "10.112.6.12:" + protocolHolder.get().getPort() + _interface + "?"
                + "application=" + applicationHolder.get().getName() + "&dubbo=2.5.3&interface=" + _interface
                + "&methods=say&pid=6816&side=provider&timestamp=1522284835101";
        System.err.println("准备开始 导出 >>" + url);
    }

    private void wakeupHolder() {
        // 利用spring context 根据id 来找 dubbo bean标签
        applicationHolder.setValue(getApplicationContext());
        protocolHolder.setValue(getApplicationContext());
    }
}

The above is the search and export of dubbo's simple service tag, which is basically the same as the idea of ​​dubbo source code implementation. There is no project source code posted above, you can add me qq: 657455400 or download it yourself: https://download.csdn.net/download/luozheng4698729 /10387658

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325190598&siteId=291194637