3. Dubbo源码解析之服务引用过程

3. Dubbo源码解析之服务引用过程

服务引用过程主要包括。。。。

3.1 Dubbo自定义xml解析器

服务暴露过程中已经讲过,此处略过。

3.2 ReferenceBean 装配与调用

完成了对xml标签的解析后,Spring会通过registerBeanPostProcessors(beanFactory)对 ReferenceBean 进行初始化,与 ServiceBean 在finishBeanFactoryInitialization(beanFactory); 步骤中初始化不同,具体原因是:ReferenceBean 实现了 FactoryBean 接口,因此 ReferenceBean 是一个 FactoryBean,需要用bean的引用去初始化,即getObject()。看下具体调用过程:

AbstractApplicationContext#registerBeanPostProcessors
    |- AbstractBeanFactory#getBean
        |- AbstractAutowireCapableBeanFactory#createBean
            |- AbstractAutowireCapableBeanFactory#doCreateBean
                |- AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod
                    |- ConstructorResolver#createArgumentArray
                        |- ConstructorResolver#resolveAutowiredArgument
                            |- DefaultListableBeanFactory#resolveDependency
                                |- DefaultListableBeanFactory#doResolveDependency
                                    |- DefaultListableBeanFactory#findAutowireCandidates
                                        |- 下面接着。。。
                                        BeanFactoryUtils#beanNamesForTypeIncludingAncestors
    |- AbstractBeanFactory#getTypeForFactoryBean
        |- AbstractAutowireCapableBeanFactory#getSingletonFactoryBeanForTypeCheck
            |- AbstractAutowireCapableBeanFactory#createBeanInstance
                |- AbstractAutowireCapableBeanFactory#instantiateBean
                    |-SimpleInstantiationStrategy#instantiate
                        |- BeanUtils.instantiateClass(constructorToUse);

从调用过程中分析 ServiceBean 与 ReferenceBean 进入代码分支不同的地方,发现在 AbstractBeanFactory#getTypeForFactoryBean 调用步骤时不同,ServiceBean 不会执行到这步,原因是ServiceBean 不是FactoryBean,代码如下:

// Check bean class whether we're dealing with a FactoryBean.
if (FactoryBean.class.isAssignableFrom(beanType)) {
    if (!BeanFactoryUtils.isFactoryDereference(name)) {
        // If it's a FactoryBean, we want to look at what it creates, not the factory class.
        beanType = getTypeForFactoryBean(beanName, mbd);
        if (beanType == null) {
            return false;
        }
    }
}

上述步骤仅仅完成 ReferenceBean 的初始化,因为它是一个 FactoryBean,所以会调用 getObject 获取引用Bean创造的对象,看下getObject是什么时候调用的?

AbstractApplicationContext#finishBeanFactoryInitialization
    |- AbstractBeanFactory#createBean
      ...
        |- AbstractBeanFactory#doGetBean
            |- AbstractBeanFactory#getObjectForBeanInstance
                |- FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
                    |- factory.getObject()

FactoryBeanRegistrySupport 提供了一些FactoryBean的相关方法供BeanFactory内部使用。FactoryBean生产的单例在 FactoryBeanRegistrySupport 成员变量 private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16); 中,

题外话:FactoryBean和FactoryBean生成的Bean的辨析

FactoryBean FactoryBean生成的Bean
种类 FactoryBean是一个普通Bean,它和普通的Bean总体上没有区别 FactoryBean生成的Bean是一种特殊的Bean
创建途径 通过CreateBean方法创建 通过FactoryBean的getObject方法
是否是单例 配置决定 由FactoryBean的isSingleton方法决定的
单例存放点 DefaultSingletonBeanRegistry的SingletonObjects FactoryBeanRegistrySupport的factoryBeanObjectCache
创建方式 可以像普通Bean进行依赖注入 因为是通过getObject方法生成,无法直接注入依赖
获取方法 需要在BeanName前加 '&' 直接用BeanName

3.3 服务引用过程

服务引用过程就是执行 getObject()过程,如果dubbo配置引用时,设置了初始化 init="true",则会在初始化时执行该方法。

<dubbo:reference init="true" id="demoFacade" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" version="1.0" group="${dubbo.consumer.group}"/>
if (shouldInit()) {
    getObject();
}

服务引用分为两种引用方式,第一种是使用服务直连的方式引用服务,第二种方式是基于注册中心进行引用。服务直连的方式仅适合在调试或测试服务的场景下使用,不适合在线上环境使用。

直连方式引用服务配置:

<dubbo:reference init="true" id="demoFacade" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" version="1.0" group="${dubbo.consumer.group}" url="dubbo://localhost:20882" />

添加URL配置就行。

3.3.1 处理配置

public void checkAndUpdateSubConfigs() {
    // 用于检测 provider、application 等核心配置类对象是否为空,
    // 若为空,则尝试从其他配置类对象中获取相应的实例。
    completeCompoundConfigs();
    // 启动配置中心
    startConfigCenter();
    // 如果没有ConsumerConfig创建一个默认的
    checkDefault();
    // 刷新ReferenceConfig配置
    this.refresh();
    if (getGeneric() == null && getConsumer() != null) {
        setGeneric(getConsumer().getGeneric());
    }
    if (ProtocolUtils.isGeneric(getGeneric())) {
        interfaceClass = GenericService.class;
    } else {
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                                           .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        checkInterfaceAndMethods(interfaceClass, methods);
    }
    resolveFile();
    // 如果没有ApplicationConfig创建一个默认的
    checkApplication();
    // 检查MetadataReportConfig,没有创建一个默认的
    checkMetadataReport();
}
private void init() {
    ...
    // 
    checkStubAndLocal(interfaceClass);
    checkMock(interfaceClass);
    ...
}

对配置检查逻辑简单总结如下:

  • 对一些配置进行填充和检查,只支持覆盖config类中显式定义的属性,不支持覆盖存储在“parameters”中的自定义参数。
  • 启动配置中心,从dubbo2.7开始支持了配置中心动态修改配置
  • 判断consumer是不是 GenericService,即泛化引用
  • 如果配置了stub=true,检测是否存在存根类,检查存根类构造函数是否有且只有一个接口类参数
  • 检测消费端Mock类是否合法
private void init() {
    // 非泛化服务
    if (!isGeneric()) {
        ...
        // 获取方法列表,添加到 map 中
        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if (methods.length == 0) {
            map.put("methods", Constants.ANY_VALUE);
        } else {
            map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }
    ...
    // MethodConfig 遍历,可以配置重试次数
    if (CollectionUtils.isNotEmpty(methods)) {
        attributes = new HashMap<String, Object>();
        for (MethodConfig methodConfig : methods) {
            ...
            // 设置事件通知配置,比如 onreturn,onthrow,oninvoke等
            attributes.put(methodConfig.getName(), 
                           convertMethodConfig2AyncInfo(methodConfig));
        }
    }

    // 创建代理类
    ref = createProxy(map);
    // 创建服务key
    String serviceKey = URL.buildKey(interfaceName, group, version);
    ...
}

在服务引用初始化时,还进行了接口方法重试次数配置,事件通知配置等。

3.3.2 引用服务

createProxy 创建代理类,实现的主要功能:判断是本地引用还是远程引用,判断是直连引用还是注册中心引用,获取Invoker(多个时 Cluster 合并处理),创建代理类。

private T createProxy(Map<String, String> map) {
    // 本地引用
    if (shouldJvmRefer(map)) {
        ...
    }
    // 远程引用
    else {
        // 直连引用,调试用
        if (url != null && url.length() > 0) {
            ...
        }
        // 注册中心引用
        else {
            checkRegistry();
            // 加载注册中心URL
            List<URL> us = loadRegistries(false);
            ...
        }
        // 单个注册中心或者单个服务直连
        if (urls.size() == 1) {
            // 构建invoker (RegistryProtocol)
            invoker = refprotocol.refer(interfaceClass, urls.get(0));
        } 
        // 多个注册中心或者多个服务直连,或者两者混合
        else {
            List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
            URL registryURL = null;
            // 获取所有的Invoker
            for (URL url : urls) {
                invokers.add(refprotocol.refer(interfaceClass, url));
                ...
            }
            if (registryURL != null) {
                ...
                // 合并invoker,创建 StaticDirecory 服务字典
                invoker = cluster.join(new StaticDirectory(u, invokers));
            } else {
                invoker = cluster.join(new StaticDirectory(invokers));
            }
        }
    }
    // 可用性检查
    // 元数据注册
    // 生成代理类
    return (T) proxyFactory.getProxy(invoker);
}
3.3.2.1 创建Invoker

Invoker是Dubbo的核心模型,代表一个可执行体,在服务提供方,用于调用服务提供类,在消费方,Invoker用于远程调用。invoker = refprotocol.refer(interfaceClass, urls.get(0)); 创建invoker,动态代理产生的代码逻辑如下:

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    ...

    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        ...
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }
}

此处,ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName) 会获取协议,具体调用过程是QosProtocolWrapper.refer() -> ProtocolListenerWrapper.refer() -> ProtocolFilterWrapper.refer() -> RegisryProtocol.refer() ,具体调用逻辑是:

private T createExtension(String name) {
    T instance = (T) EXTENSION_INSTANCES.get(clazz);
    if (instance == null) {
        EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
        instance = (T) EXTENSION_INSTANCES.get(clazz);
    }
    // registryProtocol 注入过程
    injectExtension(instance);
    // wrapper 注入过程
    Set<Class<?>> wrapperClasses = cachedWrapperClasses;
    if (CollectionUtils.isNotEmpty(wrapperClasses)) {
        for (Class<?> wrapperClass : wrapperClasses) {
            instance = injectExtension((T) 
                                wrapperClass.getConstructor(type).newInstance(instance));
        }
    }
    return instance;
}
3-3-2-1.png
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    // protocol: registry  -> zookeeper
    url = URLBuilder.from(url)
        .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
        .removeParameter(REGISTRY_KEY)
        .build();
    ...
    // group="a,b" or group="*"
    Map<String, String> qs = 
        StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
    String group = qs.get(Constants.GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    return doRefer(cluster, registry, type, url);
}

若配置的group形式是 group=“a,b” or group="*",则调用getMergeableCluster方法,即MergeableCluster实例,否则(即只配置了一个group)会生成Cluster的Adaptive字节码。

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    //${RegistryDirectory 创建与赋值}
    
    //${subscribeUrl 创建}
    
    // 注册服务引用
    registry.register(directory.getRegisteredConsumerUrl());
    // 建立路由链, 赋值到 RegistryDirectory 中
    directory.buildRouterChain(subscribeUrl);
    // 订阅(创建或共享消费者客户端,创建dubboInvoker)
    directory.subscribe(
        subscribeUrl.addParameter(CATEGORY_KEY,
                         PROVIDERS_CATEGORY + "," + 
                                  CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
    // 创建invoker
    Invoker invoker = cluster.join(directory);
    // 向本地注册表,注册消费者
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}
  • 注册服务引用

    registry获取的是zookeeper协议,因此用的是 ZookeeperRegistry,父类是FailbackRegistry

    FailbackRegistry#register
        |- ZookeeperRegistry#doRegister
          |- zkClient.create(toUrlPath(url), 
                               url.getParameter(Constants.DYNAMIC_KEY, true));
    

    将consumer的URL注册到zk,具体内容如下:

    consumer://10.216.128.81/com.gmr.dubbo.provider.remote.facade.DemoFacade?application=dubbo-demo-consumer&category=consumers&check=false&default.check=false&default.cluster=failfast&default.group=test&default.lazy=false&default.reference.filter=-monitor&default.sticky=false&default.timeout=10000&default.version=1.0&dubbo=2.0.2&environment=test&group=test&init=true&interface=com.gmr.dubbo.provider.remote.facade.DemoFacade&lazy=false&logger=slf4j&methods=sayHello&organization=decision&owner=hzguomeiran&pid=23624&release=2.7.1&revision=1.0.20190524.local-SNAPSHOT&side=consumer&sticky=false&timestamp=1561118213732&version=1.0
    
  • 建立路由链

    订阅url,如下:

    consumer://10.216.128.81/com.gmr.dubbo.provider.remote.facade.DemoFacade?application=dubbo-demo-consumer&default.check=false&default.cluster=failfast&default.group=test&default.lazy=false&default.reference.filter=-monitor&default.sticky=false&default.timeout=10000&default.version=1.0&dubbo=2.0.2&environment=test&group=test&init=true&interface=com.gmr.dubbo.provider.remote.facade.DemoFacade&lazy=false&logger=slf4j&methods=sayHello&organization=decision&owner=hzguomeiran&pid=23624&release=2.7.1&revision=1.0.20190524.local-SNAPSHOT&side=consumer&sticky=false&timestamp=1561118213732&version=1.0
    
    private RouterChain(URL url) {
        List<RouterFactory> extensionFactories = 
            ExtensionLoader.getExtensionLoader(RouterFactory.class)
                  .getActivateExtension(url, (String[]) null);
    
        List<Router> routers = extensionFactories.stream()
            .map(factory -> factory.getRouter(url))
            .collect(Collectors.toList());
    
        initWithRouters(routers);
    }
    

    创建了路由链如下:

3-3-2-2.png
  • 订阅,创建或共享消费者客户端

    与注册consumer一样,用的是ZookeeperRegistry,代码如下:

    protected void doSubscribe(final URL url, final NotifyListener listener) {
        //如果provider的service的接口配置的是“*”
        if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
            //获取服务分组根路径
            String root = toRootPath();
            //获取服务的NotifyListener
            ConcurrentMap<NotifyListener, ChildListener> listeners = 
                zkListeners.get(url);
            if (listeners == null) {
                //如果没有则创建一个
                zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener,
                                        ChildListener>());
                listeners = zkListeners.get(url);
            }
            ChildListener zkListener = listeners.get(listener);
            //如果没有子监听器则创建一个
            if (zkListener == null) {
                listeners.putIfAbsent(listener, new ChildListener() {
                    public void childChanged(String parentPath,
                                             List<String> currentChilds) {
                        for (String child : currentChilds) {
                            child = URL.decode(child);
                            if (! anyServices.contains(child)) {
                                anyServices.add(child); 
                                subscribe(url.setPath(child)
                                          .addParameters(Constants.INTERFACE_KEY, 
                                                         child, 
                                                         Constants.CHECK_KEY, 
                                                         String.valueOf(false)),
                                          listener);
                            }
                        }
                    }
                });
                zkListener = listeners.get(listener);
            }
            //向服务器订阅服务,注册中心会调用NotifyListener的notify函数返回服务列表
            zkClient.create(root, false);
            //获取服务地址列表
            List<String> services = zkClient.addChildListener(root, zkListener);
            if (services != null && services.size() > 0) {
                //如果存在服务
                for (String service : services) {
                    service = URL.decode(service);
                    anyServices.add(service);
                    //如果serviceInterface是“*”则从分组根路径遍历service并订阅所有服务
                    subscribe(url.setPath(service)
                              .addParameters(Constants.INTERFACE_KEY,
                                             service, 
                                             Constants.CHECK_KEY, 
                                             String.valueOf(false)), listener);
                }
            }
        } else {
            //如果serviceInterface不是“*”
            // 则创建Zookeeper客户端索取服务列表,并通知(notify)消费者(consumer)这些服务可以用了
            List<URL> urls = new ArrayList<URL>();
            //获取类似于http://xxx.xxx.xxx.xxx/context/com.service.xxxService/consumer的地址
            for (String path : toCategoriesPath(url)) {
                //获取例如com.service.xxxService对应的NotifyListener map
                ConcurrentMap<NotifyListener, ChildListener> listeners = 
                    zkListeners.get(url);
                if (listeners == null) {
                    zkListeners.putIfAbsent(url, 
                                            new ConcurrentHashMap<NotifyListener, 
                                            ChildListener>());
                    listeners = zkListeners.get(url);
                }
                //获取ChildListener
                ChildListener zkListener = listeners.get(listener);
                if (zkListener == null) {
                    listeners.putIfAbsent(listener, new ChildListener() {
                        public void childChanged(String parentPath,
                                                 List<String> currentChilds) {
                            ZookeeperRegistry.this.notify(url,listener, 
                                          toUrlsWithEmpty(url,
                                                          parentPath,
                                                          currentChilds));
                        }
                    });
                    zkListener = listeners.get(listener);
                }
                //创建Zookeeper客户端
                zkClient.create(path, false);
                List<String> children = zkClient.addChildListener(path, zkListener);
                if (children != null) {
                    urls.addAll(toUrlsWithEmpty(url, path, children));
                }
            }
            //提醒消费者
            notify(url, listener, urls);
        }
    }
    

    其实做了两件事,一个是创建ChildListener监听器,ZK注册监听器,当发生变化时,消费者可以监听到,监听到后也是通知消费者,另一个通知消费者,服务可以用了,最终调用的时服务字典(RegistryDirectory)的notify方法:

    public synchronized void notify(List<URL> urls) {
        Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)
            .filter(this::isValidCategory)
            .filter(this::isNotCompatibleFor26x)
            .collect(Collectors.groupingBy(url -> {
                if (UrlUtils.isConfigurator(url)) {
                    return CONFIGURATORS_CATEGORY;
                } else if (UrlUtils.isRoute(url)) {
                    return ROUTERS_CATEGORY;
                } else if (UrlUtils.isProvider(url)) {
                    return PROVIDERS_CATEGORY;
                }
                return "";
            }));
    
        // configurator 路径
        List<URL> configuratorURLs = 
            categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, 
                                      Collections.emptyList());
        this.configurators = 
            Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
      // routers 路径
        List<URL> routerURLs = 
            categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
        toRouters(routerURLs).ifPresent(this::addRouters);
    
        // providers 路径
        List<URL> providerURLs = 
            categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
        // 只刷新providers
        refreshOverrideAndInvoker(providerURLs);
    }
    
    refreshOverrideAndInvoker -> refreshInvoker -> toInvokers
     -> invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), // dubbo 协议
                                        url, providerUrl);
     -> dubboProtocol.refer
    
    public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);
        // create rpc invoker.
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
        return invoker;
    }
    

    DubboProtocol 的 refer方法创建了 DubboInvoker,其中调用了getClients(url) 方法,创建了消费者客户端。

    Dubbo是怎么解决粘包和半包的,用的是自定义的协议 DubboCodec 方式:

    bootstrap.handler(new ChannelInitializer() {
    
        @Override
        protected void initChannel(Channel ch) throws Exception {
            int heartbeatInterval = UrlUtils.getHeartbeat(getUrl());
            NettyCodecAdapter adapter = 
                new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
            ch.pipeline()
                .addLast("decoder", adapter.getDecoder())
                .addLast("encoder", adapter.getEncoder())
                .addLast("client-idle-handler", new IdleStateHandler(heartbeatInterval, 0, 0, MILLISECONDS))
                .addLast("handler", nettyClientHandler);
        }
    });
    

    将创建好的DubboInvoker 放到 RegistryDirectory 的 newUrlInvokerMap 成员变量中,

    private volatile Map<String, Invoker<T>> urlInvokerMap;

    注意:创建Netty客户端时,会根据提供者的IP和端口不同,会创建多个netty客户端,不同的接口可以共享一个netty客户端。

    此外,在 refreshOverrideAndInvoker 方法中,还会设置RouterChain 的Invokers,在服务调用,路由时会用到,如果设置了多个组,还会合并invoker。

    routerChain.setInvokers(newInvokers);
    this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
    this.urlInvokerMap = newUrlInvokerMap;
    
  • 集群获取invoker

    public class Cluster$Adpative implements Cluster {
        
        public Invoker join(Directory arg0) throws Directory {
            ...
                URL url = arg0.getUrl();
            String extName = url.getParameter("cluster", "failover");
            Cluster extension = 
                ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(extName);
            return extension.join(arg0);
        }
    }
    

    这里只是对获取一个封装了 RegistryDirectory 的集群策略的invoker,比如 FailfastClusterInvoker

    public class FailfastCluster implements Cluster {
    
        public final static String NAME = "failfast";
    
        @Override
        public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
            return new FailfastClusterInvoker<T>(directory);
        }
    
    }
    

    最终创建的是MockClusterInvoker,如下图:

3-3-2-3.png
  • 注册消费者

    public static void registerConsumer(Invoker invoker, URL registryUrl, URL consumerUrl, RegistryDirectory registryDirectory) {
        ConsumerInvokerWrapper wrapperInvoker = 
            new ConsumerInvokerWrapper(invoker, 
                                       registryUrl, consumerUrl, registryDirectory);
        String serviceUniqueName = consumerUrl.getServiceKey();
        Set<ConsumerInvokerWrapper> invokers = 
            consumerInvokers.get(serviceUniqueName);
        if (invokers == null) {
            consumerInvokers.putIfAbsent(serviceUniqueName,
                                         new ConcurrentHashSet<ConsumerInvokerWrapper>());
            invokers = consumerInvokers.get(serviceUniqueName);
        }
        invokers.add(wrapperInvoker);
    }
    

    创建 ConsumerInvokerWrapper,将消费者注册到本地缓存中。

    public static ConcurrentHashMap<String, Set<ConsumerInvokerWrapper>> consumerInvokers = new ConcurrentHashMap<>();
    
3.3.2.2 创建代理

通过 proxyFactory.getProxy(invoker); 为服务接口创建代理类,最终会调用 JavassistProxyFactory 的 getProxy 方法:

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.getProxy(interfaces)
        .newInstance(new InvokerInvocationHandler(invoker));
}

创建 InvokerInvocationHandler,实现自JDK 的 InvocationHandler接口,拦截接口类的调用。

最终会调用proxy类的getProxy静态方法,利用 ClassGenerator 进行代理类创建

public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {
    // ${遍历接口列表,校验接口,拼接接口全限定名}
    ...
    synchronized (cache) {
        do {
            // ${从缓存中获取 Reference<Proxy> 实例}
            
            // 并发控制,保证只有一个线程可以进行后续操作
            if (value == PendingGenerationMarker) {
                try {
                    cache.wait();
                } catch (InterruptedException e) {
                }
            }
        } while (true);
    }

    ClassGenerator ccp = null, ccm = null;
    try {
        // 创建 ClassGenerator 对象
        ccp = ClassGenerator.newInstance(cl);
        
        // ${装配 ccp}
        // 生成接口代理类
        Class<?> clazz = ccp.toClass();
        clazz.getField("methods").set(null, methods.toArray(new Method[0]));
        ccm = ClassGenerator.newInstance(cl);
        // // 生成 Proxy 实现类
        Class<?> pc = ccm.toClass();
        // 通过反射创建 Proxy 实例
        proxy = (Proxy) pc.newInstance();
    } catch (RuntimeException e) {
        ...
    } finally {
        ...
        synchronized (cache) {
            if (proxy == null) {
                cache.remove(key);
            } else {
                // 写缓存
                cache.put(key, new WeakReference<Proxy>(proxy));
            }
            // 唤醒其他等待线程
            cache.notifyAll();
        }
    }
    return proxy;
}

ccp 用于为服务接口生成代理类,比如我们有一个 DemoService 接口,这个接口代理类就是由 ccp 生成的。ccm 则是用于为 org.apache.dubbo.common.bytecode.Proxy 抽象类生成子类,主要是实现 Proxy 类的抽象方法。下面以 org.apache.dubbo.demo.DemoService 这个接口为例,来看一下该接口代理类代码大致是怎样的(忽略 EchoService 接口)。

package org.apache.dubbo.common.bytecode;

public class proxy0 implements org.apache.dubbo.demo.DemoService {

    public static java.lang.reflect.Method[] methods;

    private java.lang.reflect.InvocationHandler handler;

    public proxy0() {
    }

    public proxy0(java.lang.reflect.InvocationHandler arg0) {
        handler = $1;
    }

    public java.lang.String sayHello(java.lang.String arg0) {
        Object[] args = new Object[1];
        args[0] = ($w) $1;
        Object ret = handler.invoke(this, methods[0], args);
        return (java.lang.String) ret;
    }
}

发现消费者调用时,其实用的是 InvokerInvocationHandler 进行处理的。

3-3-2-4.png
<dubbo:reference init="true" id="demoFacade" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" version="1.0" group="${dubbo.consumer.group}"/>

其实就是 ReferenceBean 的接口代理引用 ref

/**
 * The interface proxy reference
 */
private transient volatile T ref;
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容