Dubbo source code reading 6: Dubbo dynamically generated classes

Section 1 Dubbo dynamically generated classes

When dubbo calls the exportServices() method, it will use code generation technology to dynamically generate the subclasses of Protocol$Adaptive, ProxyFactory$Adaptive and Wrapper.

Dubbo uses javassist to dynamically generate classes. Dubbo has two scenarios using dynamic classes.

1)ExtensionLoader.getExtensionLoader().getAdaptiveExtension() uses JavassistCompiler to generate Adaptive classes of each type. Protocol$Adaptive, ProxyFactory$Adaptive and other classes are generated by this method.

2)ServiceConfig.doExportUrlsFor1Protocol() uses javassistproxyfactory The getinvoker () method calls Wrapper The makewrapper () method generates Wrapper subclasses for < Dubbo: Service > in xml.

Section 2 Protocol$Adaptive class

GreetingService is defined as follows.

package org.example.dubbo2.provider.api;
public interface GreetingService {
    String sayHi(String name);
    String sayBye(String name);
}

The implementation of GreetingServiceImpl is as follows.

package org.example.dubbe2.provider.impl;
import org.example.dubbo2.provider.api.GreetingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GreetingServiceImpl implements GreetingService {
    private static final Logger logger = LoggerFactory.getLogger(GreetingServiceImpl.class);
    @Override
    public String sayHi(String name) {
        logger.info(name);
        return "hi, " + name;
    }
    @Override
    public String sayBye(String name) {
        logger.info(name);
        return "bye, " + name;
    }
}

The startup classes are as follows.

package org.example.dubbe2.provider.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
@Configuration
public class Dubbo2ProviderApplication {
    private static final Logger logger = LoggerFactory.getLogger(Dubbo2ProviderApplication.class);
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"/spring/dubbo-provider.xml"});
        context.start();
        System.in.read(); // press any key to exit
    }
}

dubbo-provider. The XML content is as follows.

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <dubbo:application name="dubbo2-provider"/>
    <dubbo:registry address="zookeeper://172.16.4.126:2181"/>
    <dubbo:metadata-report address="zookeeper://172.16.4.126:2181"/>
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:service interface="org.example.dubbo2.provider.api.GreetingService" ref="greetingService"/>
    <bean id="greetingService" class="org.example.dubbe2.provider.impl.GreetingServiceImpl"/>
</beans>

ServiceConfig is defined as follows (excluding other information). When the JVM loads the ServiceConfig class, it initializes its two static static fields.

public class ServiceConfig<T> extends ServiceConfigBase<T> {
    private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
}

When calling extensionloader When getadapteextension() method is used, createadapteextensionclass() method will be executed to dynamically generate Protocol$Adaptive and ProxyFactoryAdaptive classes. Add a breakpoint in the createadaptive extensionclass () code, start the program to the breakpoint, and check the value of the code, as shown in the following figure.

After the code value is copied and formatted, the dynamically generated Protocol$Adaptive class code is as follows.

package org.apache.dubbo.rpc;

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

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    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());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        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);
    }

    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

    public java.util.List getServers() {
        throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
}

 

Section 3 ProxyFactory$Adaptive class

Dynamically copy the generated value of $factory class after formatting.

package org.apache.dubbo.rpc;

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

public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
    public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
        if (arg2 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
    }

    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0, arg1);
    }

    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0);
    }
}

Section 4 ProxyFactory Extension instance

org.apache.dubbo.rpc.ProxyFactory configuration is as follows:

stub=org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory

These classes all implement the proxyfactory interface.

When instantiating the JavassistProxyFactory object, the extesionloader loader of the ProxyFactory class will decorate the JavassistProxyProxyFactory instance with a StubProxyFactoryWrapper. Its purpose is to use the decorator pattern to realize the function of AOP.

Section 5 Wrapper subclass

In serviceconfig The doExportUrlsFor1Protocol () method invokes exportLocal() to increase the breakpoint, as shown in the following diagram, and the startup program executes here, then F7 executes into the exportLocal() method.

Execute to protocol export(PROXY_FACTORY.getInvoker(ref, interfaceClass, local)).

 

Then, call F7 to enter javassistproxfactory Getinvoker () method. Go down to wrapper Makewrapper() method.

 

In wrapper CC. In makewrapper() method Toclass() adds a breakpoint, runs here, and then press F7 to enter classgenerator Toclass () method, and then enter classgenerator Toclass (classloader, loader, protectiondomain PD) method, run F8 step by step until return mctc toClass(laoder, pd).

 

 

 

Open the Evaluate dialog box in the IDEA and enter the following code in the Expression edit box to write the dynamically generated CtClass object to local 1 Class file.

FileOutputStream out1 = new FileOutputStream("D:\\1.class");out1.write(mCtc.toBytecode());out1.close();return "";

Then find this 1 Class file, select it and drag it into the IDEA edit box to view the source code content corresponding to the class file.

<dubbo:service interface="org.example.dubbo2.provider.api.GreetingService" ref="greetingService"/>
<bean id="greetingService" class="org.example.dubbe2.provider.impl.GreetingServiceImpl"/>

The dynamic classes generated for the above < Dubbo: Service > tags in xml are as follows, which are subclasses of Wrapper class.

package org.apache.dubbo.common.bytecode;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.ClassGenerator.DC;
import org.example.dubbe2.provider.impl.GreetingServiceImpl;

public class Wrapper1 extends Wrapper implements DC {
    public static String[] pns;
    public static Map pts;
    public static String[] mns;
    public static String[] dmns;
    public static Class[] mts0;
    public static Class[] mts1;

    public String[] getPropertyNames() {
        return pns;
    }

    public boolean hasProperty(String var1) {
        return pts.containsKey(var1);
    }

    public Class getPropertyType(String var1) {
        return (Class)pts.get(var1);
    }

    public String[] getMethodNames() {
        return mns;
    }

    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    public void setPropertyValue(Object var1, String var2, Object var3) {
        try {
            GreetingServiceImpl var4 = (GreetingServiceImpl)var1;
        } catch (Throwable var6) {
            throw new IllegalArgumentException(var6);
        }

        throw new NoSuchPropertyException("Not found property \"" + var2 + "\" field or setter method in class org.example.dubbe2.provider.impl.GreetingServiceImpl.");
    }

    public Object getPropertyValue(Object var1, String var2) {
        try {
            GreetingServiceImpl var3 = (GreetingServiceImpl)var1;
        } catch (Throwable var5) {
            throw new IllegalArgumentException(var5);
        }

        throw new NoSuchPropertyException("Not found property \"" + var2 + "\" field or getter method in class org.example.dubbe2.provider.impl.GreetingServiceImpl.");
    }

    public Object invokeMethod(Object var1, String var2, Class[] var3, Object[] var4) throws InvocationTargetException {
        GreetingServiceImpl var5;
        try {
            var5 = (GreetingServiceImpl)var1;
        } catch (Throwable var8) {
            throw new IllegalArgumentException(var8);
        }

        try {
            if ("sayHi".equals(var2) && var3.length == 1) {
                return var5.sayHi((String)var4[0]);
            }

            if ("sayBye".equals(var2) && var3.length == 1) {
                return var5.sayBye((String)var4[0]);
            }
        } catch (Throwable var9) {
            throw new InvocationTargetException(var9);
        }

        throw new NoSuchMethodException("Not found method \"" + var2 + "\" in class org.example.dubbe2.provider.impl.GreetingServiceImpl.");
    }

    public Wrapper1() {
    }
}

Section 6 Protocol Extension instance

org. apache. dubbo. rpc. The protocol configuration is as follows:

filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
rmi=org.apache.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=org.apache.dubbo.rpc.protocol.hessian.HessianProtocol
webservice=org.apache.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=org.apache.dubbo.rpc.protocol.thrift.ThriftProtocol
native-thrift=org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocol
memcached=org.apache.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=org.apache.dubbo.rpc.protocol.redis.RedisProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
xmlrpc=org.apache.dubbo.xml.rpc.protocol.xmlrpc.XmlRpcProtocol
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper

These classes implement the Protocol interface.

ProtocolFilterWrapper, ProtocolListenerWrapper and QosProtocolWrapper are three decorator classes used to decorate other Protocol instances. The decoration sequence is as follows

QosProtocolWrapper
  -> ProtocolFilterWrapper
     -> ProtocolListenerWrapper
       -> other Protocol Implementation class

Section 7 Filter Active Extension instance

org. apache. dubbo. rpc. The filter configuration is as follows:

cache=org.apache.dubbo.cache.filter.CacheFilter
validation=org.apache.dubbo.validation.filter.ValidationFilter
echo=org.apache.dubbo.rpc.filter.EchoFilter
generic=org.apache.dubbo.rpc.filter.GenericFilter
genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
token=org.apache.dubbo.rpc.filter.TokenFilter
accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter
context=org.apache.dubbo.rpc.filter.ContextFilter
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
exception=org.apache.dubbo.rpc.filter.ExceptionFilter
executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter
tps=org.apache.dubbo.rpc.filter.TpsLimitFilter
trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
monitor=org.apache.dubbo.monitor.support.MonitorFilter

metrics=org.apache.dubbo.monitor.dubbo.MetricsFilter

Active Filter instance on provider side

ExtensionLoader. getExtensionLoader(Filter.class). Getactivateextension (invoker. Geturl(), "service. Filter", "provider") obtains the following valid filter instances on the provider side:

filters = {ArrayList@4255}  size = 8
 0 = {EchoFilter@5306} 
 1 = {ClassLoaderFilter@5307} 
 2 = {GenericFilter@5308} 
 3 = {ContextFilter@5309} 
 4 = {TraceFilter@5310} 
 5 = {TimeoutFilter@5311} 
 6 = {MonitorFilter@5312} 
 7 = {ExceptionFilter@5313} 

Package the invoker with filter chain. The FilterNode chain is shown in the figure below

 

Keywords: Java rpc Network Protocol

Added by waffle72 on Sat, 12 Feb 2022 01:49:11 +0200