Dubbo source code — the Wrapper details

Wrapper

Wrapper is a wrapper object for the Dubbo SPI extension class that acts as a proxy. When Dubbo parses the SPI configuration, any associated wrapper classes are cached in cachedWrapperClasses, and when getExtension is executed, the wrapper class for that extended class is returned if it is needed.

Wrapper annotations

Dubbo provides Wrapper annotations, which indicate that all extension classes of the extension will be propped up by the Wrapper class if there is no Wrapper class. Otherwise, you need to specify either the extension class that requires a proxy with matches or the extension class that does not need a proxy with mismatches on the annotation, based on the extension class name of the extension.

Source code analysis

Wrapper class resolution
private void loadClass(Map<String, Class<? >> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,boolean overridden) throws NoSuchMethodException {
    // Ignore extraneous code here
  
    // Check if there is an @adaptive annotation on the target class
    if (clazz.isAnnotationPresent(Adaptive.class)) {
     // Ignore extraneous code here
     // Check whether the class is of wrapper type
    } else if (isWrapperClass(clazz)) {
        // Set the cache
        cacheWrapperClass(clazz);
    // This is a normal extension class
    } else {
        // Ignore extraneous code here}}Copy the code

LoadClass is a method that is called when the Dubbo SPI configuration file is parsed. Its entry is the getExtensionClasses method in ExtensionLoader. If there is an extension class in the SPI configuration file that meets the Wrapper Wrapper class convention, it will be added to the cache. Let’s look at how the Wrapper Wrapper class convention is implemented and added to the cache.

private boolean isWrapperClass(Class
        clazz) {
    try {
        clazz.getConstructor(type);
        return true;
    } catch (NoSuchMethodException e) {
        return false; }}private void cacheWrapperClass(Class
        clazz) {
    if (cachedWrapperClasses == null) {
      cachedWrapperClasses = new ConcurrentHashSet<>();
    }
    cachedWrapperClasses.add(clazz);
 }
Copy the code

As you can see from the isWrapperClass method, the convention for a wrapped class is that the class must have a constructor that takes the extended class. As you can see from its caching method cacheWrapperClass, an extended class can have multiple Wrapper classes.

Wrapper class
private T createExtension(String name, boolean wrap) {
    // Ignore irrelevant code
​
    try {
        // Fetch from the cache
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            // Create and add to cache
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
​
        // Attribute injection
        injectExtension(instance);
​
        // Use a wrapper class. If a wrapper class exists, return the wrapper class instead of an instance of the class
        if(wrap) { List<Class<? >> wrapperClassesList =new ArrayList<>();
            // Get all wrapper classes for this class
            if(cachedWrapperClasses ! =null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                // Sort the different wrapper classes
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }
​
            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                // If there are multiple wrapper classes, the wrapper class obtained in the previous loop will be injected into the new wrapper class through the constructor
                // This ensures that multiple wrapper classes are possible in the case of multiple wrapper classes. It is worth mentioning that the execution order can be specified for different wrapper classes
                // Can be sorted via the Activate annotation
                for(Class<? > wrapperClass : wrapperClassesList) { Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);// If the extension class uses the @wrapper annotation, that class should not use the Wrapper class
                    if (wrapper == null|| (ArrayUtils.contains(wrapper.matches(), name) && ! ArrayUtils.contains(wrapper.mismatches(), name))) {//instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); }}}}Lifecycle is implemented and the initialize() method is executed if Lifecycle is implemented
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: "+ t.getMessage(), t); }}Copy the code

The createExtension method is the method to create an Extension instance, which occurs after the Dubbo SPI configuration file has been parsed and whose entry is the getExtension method in the ExtensionLoader. If we need the Wrapper class, we will get the Wrapper class cached in the Dubbo SPI parsing phase, sort it, and start traversal. If we use reflection to instantiate the Wrapper, we will do IOC injection. Instance = injectExtension ((T) wrapperClass getConstructor (type). NewInstance (instance)), Each loop injects the last Wrapper instance as an argument to the constructor into the new Wrapper instance, thus implementing the multi-wrapper proxy of the multi-layer extension class.

Multiple Wrapper priorities

Add the @activate annotation to the Wrapper class and sort by setting order. The smaller the priority, the higher the priority

The sample

extension
@SPI("ali")
public interface Pay {
    void pay(URL url);
}
Copy the code
The extension class
public class AliPay implements Pay{
    @Override
    public void pay(URL url) {
        System.out.println("Alipay Pay"); }}Copy the code
Wrapper class
@Activate(order = 2)
public class PayWrapper1 implements Pay{
    private Pay pay;
    public PayWrapper1(Pay pay) {
        this.pay = pay;
    }
    @Override
    public void pay(URL url) {
        System.out.println("pay before...");
        pay.pay(url);
        System.out.println("pay after..."); }}@Activate(order = 1)
public class PayWrapper2 implements Pay{
​
    private Pay pay;
​
    public PayWrapper2(Pay pay) {
        this.pay = pay;
    }
​
    @Override
    public void pay(URL url) {
        System.out.println("wrapper2 pay before...");
        pay.pay(url);
        System.out.println("wrapper2 pay after..."); }}Copy the code
Dubbo SPI configuration
The file name of the Pay class is meta-inf /dubbo/
ali=org.apache.dubbo.demo.provider.javaspi.AliPay
wrapper=org.apache.dubbo.demo.provider.javaspi.PayWrapper1
Copy the code
The test class
public class JavaSpiTest {
​
    /** * Does not use the packaging class * print information for: Alipay payment */
    @Test
    public void test1(a) {
        ExtensionLoader<Pay> extensionLoader =
                ExtensionLoader.getExtensionLoader(Pay.class);
        Pay ali1 = extensionLoader.getExtension("ali".false);
        ali.pay(URL.valueOf("http://localhost:9999/xxx"));
    }
  
  Wrapper2 pay before... wrapper2 pay before... * pay before... Pay/pay/pay after... * wrapper2 pay after... * /
    @Test
    public void test2(a) {
        ExtensionLoader<Pay> extensionLoader =
                ExtensionLoader.getExtensionLoader(Pay.class);
        Pay ali1 = extensionLoader.getExtension("ali".true);
        ali.pay(URL.valueOf("http://localhost:9999/xxx")); }}Copy the code