dubbo source code analysis SPI 1 of principle and the principle of IOC and AOP

What is 1.Dubbo SPI

First look description dubbo official website:
the SPI called the Service Provider Interface, is a service discovery mechanism. SPI is essentially fully qualified class name of the interface in the configuration file, the service by the loader reads the configuration file, loading implementation class. This At run time, the interface implementation class dynamically replaced. So positive characteristics, we can easily provide expanded functionality for our program through the SPI mechanisms. SPI mechanism in the framework of the third party also has applications such as Dubbo is to load all of the components of the mechanism by SPI. However, Dubbo SPI mechanism does not use Java native, but it has been enhanced to enable it to better meet the needs. In the Dubbo, SPI is a very important module. Based on SPI, we can easily be expanded to Dubbo. If you want to learn Dubbo source, SPI sure to understand the mechanism.

2.Java SPI exemplary and Dubbo SPI

Examples refer to the official network, to the following Robot (robot) interface as an example, there are two implementation classes OptimusPrime (Prime) and Bumblebee (Hornet).

@SPI   //JavaSPI不需要这个注解,使用dubboSPI的时候需要
public interface Robot {
    void sayHello();
}

public class OptimusPrimeimplements Robot {
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}

public class Bumblebeeimplements Robot {
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Bumblebee.");
    }
}

Java SPI configuration file in the META-INF / services, the following:

com.pigcoffe.spi.Bumblebee
com.pigcoffe.spi.OptimusPrime

Dubbo SPI configuration file in the META-INF / dubbo next, as follows:

optimusPrime = com.pigcoffe.spi.OptimusPrime
bumblebee = com.pigcoffe.spi.Bumblebee

File names are interfaces full class name.
Java SPI test class:

package com.pigcoffe.spi;
import com.pigcoffe.spi.Robot;
import org.junit.Test;
import java.util.ServiceLoader;
public class JavaSPITest {
    @Test
    public void sayHello() throws Exception {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
        System.out.println("Java SPI");
        serviceLoader.forEach(Robot::sayHello);
    }
}

Output:

Java SPI
Hello, I am Bumblebee.
Hello, I am Optimus Prime.

Dubbo SPI test class, where necessary to introduce dependent dubbo

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.4</version>
        </dependency>
package com.pigcoffe.spi;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import org.junit.Test;
public class DubboSPITest {
    @Test
    public void sayHello() throws Exception {
        ExtensionLoader<Robot> extensionLoader =
            ExtensionLoader.getExtensionLoader(Robot.class);
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}

Output:

Hello, I am Optimus Prime.
Hello, I am Bumblebee.

3.Dubbo SPI What are the advantages, why not use the built-in SPI jdk

From the sample code can tell the difference between some of the

  • JDK standard SPI will be a one-time extension point to instantiate all realize, if extension initialization achieve very time-consuming, but also if the load on useless, would be a waste of resources.
  • If the extension fails to load point, even the name of the extension fail to get the point. For example: JDK the ScriptEngine standard, () Gets the name of the script by getName type, because if RubyScriptEngine depends jruby.jar absent, resulting in a failure to load class RubyScriptEngine, this failure reason is eaten, and it does not correspond to ruby, when a user performs a ruby ​​script, it does not support the ruby ​​will be reported, rather than the real cause of the failure.
  • JDK SPI does not support caching does not support default values, do not support the IOC and AOP functionality

4. source code analysis

Starting from the first line

ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");

From extensionLoader.getExtension ( "optimusPrime"); Code follow

    public T getExtension(String name) {
        if (name != null && name.length() != 0) {
            if ("true".equals(name)) {
                return this.getDefaultExtension();
            } else {
                Holder<Object> holder = (Holder)this.cachedInstances.get(name);
                if (holder == null) {
                    this.cachedInstances.putIfAbsent(name, new Holder());
                    holder = (Holder)this.cachedInstances.get(name);
                }

                Object instance = holder.get();
                if (instance == null) {
                    synchronized(holder) {
                        instance = holder.get();
                        if (instance == null) {
                            //缓存中没有,新创建
                            instance = this.createExtension(name);
                            holder.set(instance);
                        }
                    }
                }

                return instance;
            }
        } else {
            throw new IllegalArgumentException("Extension name == null");
        }
    }

Logic is simple, get the object is to start the cache, if not create this.createExtension (name) and add cache
Here is createExtension (String name) method

    private T createExtension(String name) {
//从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
        Class<?> clazz = (Class)this.getExtensionClasses().get(name);
        if (clazz == null) {
            throw this.findException(name);
        } else {
            try {
                T instance = EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = EXTENSION_INSTANCES.get(clazz);
                }
                //dubbo IOC
                this.injectExtension(instance);
                Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
                Class wrapperClass;
                if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                    for(Iterator i$ = wrapperClasses.iterator(); i$.hasNext(); 
//dubbo AOP
instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
                        wrapperClass = (Class)i$.next();
                    }
                }

                return instance;
            } catch (Throwable var7) {
                throw new IllegalStateException("Extension instance(name: " + name + ", class: " + this.type + ")  could not be instantiated: " + var7.getMessage(), var7);
            }
        }
    }

The code first is getExtensionClasses () to load the configuration file from the expansion of all classes available "configuration item name" to "configuration classes" mapping table,
ExtensionLoader # getExtensionClasses () -> loadExtensionClasses ()

private static final String SERVICES_DIRECTORY = "META-INF/services/";

    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
  private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadDirectory(extensionClasses, DUBBO_DIRECTORY);
        loadDirectory(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

Here reflect dubbo SPI configuration file directory, and where in addition to META-INF / dubbo /, as well as META-INF / dubbo / internal /, the main directory is put under internal framework dubbo own extensions, users generally put the new extension in Dubbo directory, the above example configuration file to the META-INF / dubbo / internal / directory can normally be run.

Then it comes to the IOC and AOP implementation dubbo, first enter achieve this.injectExtension (instance) IOC's

    private T injectExtension(T instance) {
        try {
            if (this.objectFactory != null) {
                Method[] arr$ = instance.getClass().getMethods();
                int len$ = arr$.length;

                for(int i$ = 0; i$ < len$; ++i$) {
                    Method method = arr$[i$];
                    if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
                        Class pt = method.getParameterTypes()[0];

                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            Object object = this.objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception var9) {
                            logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
                        }
                    }
                }
            }
        } catch (Exception var10) {
            logger.error(var10.getMessage(), var10);
        }

        return instance;
    }

Here obtained by a method reflecting object is found traversing setter injection method dependent, dependent objects obtained by the objectFactory, and two SpiExtensionFactory SpringExtensionFactory, an object is to obtain

AOP implementations in this line of code:

instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));

wapperClass have here is actually a constructor parameter is the target class interface, used here Decorator design pattern to the above Robert interface as an example, to create a new class that implements RobotCheckWapper aop function, and then check that before the robot say helle carrying weapons, as follows

package com.pigcoffe.spi;

import com.alibaba.dubbo.common.extension.Adaptive;


public class RobotCheckWapper implements Robot {
    private final Robot robot;
    public RobotCheckWapper(Robot robot){
        this.robot = robot;
    }

    @Override
    public void sayHello() {
        System.out.println("--->say hello之前检查下是否带有武器 ---->");
        robot.sayHello();
    }
}

Add a line in the configuration file:
optimusPrime = com.pigcoffe.spi.OptimusPrime
the bumblebee = com.pigcoffe.spi.Bumblebee
bumblebeeWapper = com.pigcoffe.spi.RobotCheckWapper

Before the test run DubboSPITest kind of printing is as follows:

--->say hello之前检查下是否带有武器 ---->
Hello, I am Optimus Prime.
--->say hello之前检查下是否带有武器 ---->
Hello, I am Bumblebee.

AOP is not complete and functional, or replaced logging time statistics also resembled.

5. Summary

This article describes what is SPI, what is Dubbo SPI, as well as the basic use;
the JDK What are the advantages compared to the SPI compared dubbo SPI;
a brief introduction dubbo IOC and AOP implementation principle of;
next to introduce the extension mechanism @Adaptie dubbo Notes also @Active comment

Published 52 original articles · won praise 8 · views 130 000 +

Guess you like

Origin blog.csdn.net/wufengui1315/article/details/103942916