🌟 Welcome to follow the personal technology public account: Jesse Nobu

preface

In the last section of the Apache Dubbo series annotating the @Adaptive implementation principle, I encountered a new problem, which is if we execute the following code, according to my previous article parsing

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Copy the code

At this time, a Protocol $Adaptive class will be dynamically generated and loaded. Then, when we call the method with @adaptive modification, we pass in the URL to obtain the corresponding extension according to the corresponding field.

        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = ((url.getProtocol() == null)?"dubbo"
                : url.getProtocol());
                
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
                .getExtension(extName);
Copy the code

However, in some cases, calls to methods that do not call the target extension directly may be preceded by classes such as Fitler(filter), so this mechanism is very important in Dubbo!

Are you ready? Let’s Go~

A mirror exactly

First let’s recall that when Dubbo loads a class that has not been loaded, it loads it through the getExtensionClasses method. The specific order is as follows:

  1. callgetExtensionClassesGet extension methods
  2. callloadExtensionClassesLoad extension method
  3. callloadDirectoryLoad by directory resource
  4. callloadResourceLoad by resource path
  5. callloadClassMethod loads a single class resource

The loadClass method does several things:

  1. Classes are@Adaptivemodified
  2. throughisWrapperMethod to determine if it is a wrapper class
  3. The cache@ActivateEmbellished information

We focus on wrappers in this section. Let’s look at the Wrapper code. The first is by determining whether it is an extended class

private boolean isWrapperClass(Class
        clazz) {
    try {
        clazz.getConstructor(type);
        return true;
    } catch (NoSuchMethodException e) {
        return false; }}Copy the code

The above check is whether the current clazz has a constructor that is its parent, and if so it is a wrapper class. It is then added to the ExtensionLoader member property cachedWrapperClasses. This step is mainly to detect which classes are wrapper classes.

For example, the most classic in Dubbo is Protocol. Let’s start by looking at what the Protocol extension classes are. What do you think? Remember that the Dubbo SPI extension definition file defines the file name based on the package path of the interface. So Protocol package name. The full path is com. Alibaba dubbo.. RPC Protocol. So if we look at this file, we’ll see that there are many related definitions:

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
Copy the code

There are many classes above, but only three are identified as Wrapper classes. They are

  1. ProtocolListenerWrapperThe filter chain calls the chain wrapper
  2. QosProtocolWrapperProtocol listener wrapper
  3. ProtocolFilterWrapperOnline operation and maintenance service wrapper

The createExtension method is used to create a class that has not been instantiated, so we can create cachedWrapperClasses and wrap it as follows:

private T createExtension(String name) { Class<? > clazz = getExtensionClasses().get(name);if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);
        // Get the corresponding cachedWrapperClasses from the member attributeSet<Class<? >> wrapperClasses = cachedWrapperClasses;if(wrapperClasses ! =null && !wrapperClasses.isEmpty()) {
            / / loop
            for(Class<? > wrapperClass : wrapperClasses) {// instantiate while injectinginstance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); }}return instance;
    } catch (Throwable t) {
        // throw Exception...}}Copy the code

The method focus code above has been commented out. We’re still going to use examples. Assuming we are ready to instantiate DubboProtocol, the current instance=DubboProtocol. The ProtocolListenerWrapper instances the DubboProtocol as a constructor parameter (injectExtension is mainly responsible for method injection of the set* prefix). The ProtocolListenerWrapper is then assigned to Instance; Next, instantiate QosProtocolWrapper, again instance=ProtocolListenerWrapper as a constructor argument, and assign QosProtocolWrapper to instance; Finally, there is the ProtocolFilterWrapper, and the next steps are the same as above.

When instantiated, the last instance is ProtocolFilterWrapper. The corresponding Dubbo Holder holds a ProtocolFilterWrapper. And what can we do with that? What we can actually see is that this is a nested filter chain, which you can interpret as a call that implements a chain of responsibility, or as some kind of implementation of AOP (because Spring’s IOC actually looks up beans through beanName, too, But you can implement real instances of AOP by substitution), as long as you understand it!

Now it’s time to call. Why is the criterion for determining whether or not a Wrapper is a constructor parameter? In order to make the call the same way as the class that is actually being called. Take Protocol as an example. When we call the Export method of DubboProtocol, we are actually calling the Export method of the ProtocolFilterWrapper, so we are ostensibly calling the Export method of the Protocol. Is this taking full advantage of the interface?

Let’s try to parse the above example. ProtocolFilterWrapper#export

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // Use the url protocol to check whether it is Registry
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // Set up the invoker call chain and pass it to the next one
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
Copy the code

According to the flow above, we know that the ProtocolFilterWrapper holds a QosProtocolWrapper. Then we move on to QosProtocolWrapper#export.

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // Use the url protocol to check whether it is Registry
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        startQosServer(invoker.getUrl());
        return protocol.export(invoker);
    }
    // Call the holding object directly
    return protocol.export(invoker);
}
Copy the code

We can see that the QosProtocolWrapper has formal processing code only in the Registry protocol, which actually starts a QosServer service.

Similarly, QosProtocolWrapper holds the Protocol ProtocolListenerWrapper.

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    //// check whether it is Registry by url protocol
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // Two steps
    return new ListenerExporterWrapper<T>(protocol.export(invoker),
            Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                    .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
Copy the code

The above is done in two steps:

  1. throughExtensionLoaderTo obtainExporterListenerThe plugin is then passed inurlCall the specific extension and get the return result information
  2. ProtocolListenerWrapperStudent: Holding is actuallyDubboProtocol, so willDubboProtocolThe result of the method call is wrapped with the result information obtained in the first stepListenerExporterWrapper

In the case of the Protocol, most of the access is actually to my friend, which I’ll continue in the next section!

conclusion

This episode, actually discovered while reading Dubbo service exposure, is also a foreshadows for the next section. In the next section, I will continue to parse the source code for Dubbo’s service exposure process!

See you next time!