zoukankan      html  css  js  c++  java
  • dubbo refrence bean(服务引用)

    在xml上写一个dubbo标签就可以把远程的服务引用到本地使用:

    <dubbo:reference id="buyFoodService" interface="com.test.dubbo.service.BuyFoodService"/>

    既然用spring那就是Schema了,dubbo中自定义了Schema,在DubboNamespaceHandler中:

    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    spring 中继承BeanDefinitionParser 实现自定义解析xml的规则。DubboBeanDefinitionParser内实现了解析。
    最终要生成一个对应class的BeanDefinition。BeanDefinition在spring中时bean的数据源。
    各个标签对应的pojo:
    public void init() {  
         registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(plicationConfig.class, true));  
         registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(duleConfig.class, true));  
         registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(gistryConfig.class, true));  
         registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(nitorConfig.class, true));  
         registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(oviderConfig.class, true));  
         registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(nsumerConfig.class, true));  
         registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(otocolConfig.class, true));  
         registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(rviceBean.class, true));  
         registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ferenceBean.class, false));  
         registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(notationBean.class, true));  
     } 
     
    ReferenceBean的继承结构:
    直接看afterPropertiesSet()方法,在spring中初始化好bean后会执行这个方法,方法中会注入各个组件:
    ConsumerConfig,ApplicationConfig,List<RegistryConfig>,MonitorConfig,ModuleConfig,然后是这段代码:
            Boolean b = isInit();
            if (b == null && getConsumer() != null) {
                b = getConsumer().isInit();
            }
            if (b != null && b.booleanValue()) {
                getObject();
            }

    这个init就是前面设置reference标签时的一个可选属性,如果我们设置true,那么在执行afterPropertiesSet()的时候就会执行到这个getObject()方法。

      public Object getObject() throws Exception {
            return get();
        }
    这个getObject是FactoryBean的实现,这个在spring容器中,FactoryBean跟普通Bean不同,通过BeanFactory类的getBean方法直接获取到的并不是该FactoryBean的实例,而是该FactoryBean中方法getObject返回的对象。所以当我们执行BuyFoodService buyFoodService = (BuyFoodService) context.getBean("buyFoodService");
    这样的代码是就也会执行到getObject()方法。
    getObject()方法中调用的是父类ReferenceConfig的get();然后会调用到init()方法。
     public synchronized T get() {
            if (destroyed) {
                throw new IllegalStateException("Already destroyed!");
            }
            if (ref == null) {
                init();
            }
            return ref;
        }
    看这个init方法,写得不怎么样,像膏药一样一块块散落着一地。先不管,直接看它返回的代码:
    invoker = refprotocol.refer(interfaceClass, url);
    // 创建服务代理
    return (T) proxyFactory.getProxy(invoker);
    首先protcol产生一个invoker,然后把这个invoker代理,来提供使用。
    这个proxyFactory和Protocol我们看到是这样获取的:
    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
    private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    ProxyFactory的代码:
    @SPI("javassist")
    public interface ProxyFactory {
        @Adaptive({Constants.PROXY_KEY})
        <T> T getProxy(Invoker<T> invoker) throws RpcException;
    
        @Adaptive({Constants.PROXY_KEY})
        <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
    }
    Protocol的代码:
    @SPI("dubbo")
    public interface Protocol {
        @Adaptive
        <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
        @Adaptive
        <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
        void destroy();
    
    }
    ProxyFactory用javassist作为默认实现,Protocol默认用dubbo,所以我们先关注两个子类:JavassistProxyFactory和DubboProtocol。
     
    1,DubboProtocol的refer方法返回一个DubboInvoker。
    2,JavassistProxyFactory把DubboInvoker作为被代理对象动态产生一个代理类
     
    在生成Invoker的时候依次执行的是:ProtocolListenerWrapper,ProtocolFilterWrapper,RegistryProtocol,DubboProtocol。
    在这个过程中需要像注册中心那信息,组装出存储service调用必要信息的实例。其中很多细节,后续自己研究。
     
    那么ProxyFactory是JavassistProxyFactory,其实先执行的是StubProxyFactoryWrapper,在前面文章提到过了这种机制。StubProxyFactoryWrapper的构造函数参数是ProxyFactory,这里在上篇dubbo中Listener的实现中也有涉及到。他在getProxy里做了逻辑。
    先了解下Stub(存根),在dubbo中远程调用一个服务被封装成一个本地service,一般我们都是引用接口,就可以调用到它的方法,实现则在远程的应用上,但当我们想在发起远程请求前做一些事情,比如做ThreadLocal缓存,提前验证参数,调用失败后伪造容错数据。这个就是stub要实现的事情。这段逻辑就在StubProxyFactoryWrapper的getProxy方法里。我们来看一下它代码:
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
        T proxy = proxyFactory.getProxy(invoker);
        if (GenericService.class != invoker.getInterface()) {
           // 查看有没有stub属性
            String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
            if (ConfigUtils.isNotEmpty(stub)) {
                Class<?> serviceType = invoker.getInterface();
                if (ConfigUtils.isDefault(stub)) {
                    if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {
                        stub = serviceType.getName() + "Stub";
                    } else {
                        stub = serviceType.getName() + "Local";
                    }
                }
                try {
                    Class<?> stubClass = ReflectUtils.forName(stub);
                    if (! serviceType.isAssignableFrom(stubClass)) {
                        throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + serviceType.getName());
                    }
                    try {
                        // 判断有没有参数是本service的构造函数,要有这个函数才能可用
                        Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
                        // 这里看到是直接使用constructor,并没有判断,不是很好,看起来有问题,实际在解析stub属性的时候已经做过是否包含指定构造函数,如果没有责会报错。所以这里可以放心使用,不会空指针。
                        proxy = (T) constructor.newInstance(new Object[] {proxy});
                        //export stub service
                        URL url = invoker.getUrl();
                        if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){
                            url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
                            url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
                            try{
                                export(proxy, (Class)invoker.getInterface(), url);
                            }catch (Exception e) {
                                LOGGER.error("export a stub service error.", e);
                            }
                        }
                    } catch (NoSuchMethodException e) {
                        throw new IllegalStateException("No such constructor "public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")" in stub implemention class " + stubClass.getName(), e);
                    }
                } catch (Throwable t) {
                    LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
                    // ignore
                }
            }
        }
        return proxy;
    }

    返回代理实例,所以在开头申明的BuyFoodService,在spring容器中实际指向的是一个封装好的代理。

  • 相关阅读:
    友链
    OI日常
    P4451 [国家集训队]整数的lqp拆分 生成函数
    AT4831 [ABC155F] Perils in Parallel 生成树
    P4438 [HNOI/AHOI2018]道路 树DP
    CF383E Vowels 子集DP 容斥
    P5488 差分与前缀和 生成函数+多项式EXP
    CF115E Linear Kingdom Races 线段树优化DP
    CF49E Common ancestor 区间DP
    P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II 莫队二次离线
  • 原文地址:https://www.cnblogs.com/killbug/p/7531113.html
Copyright © 2011-2022 走看看