1. JDK SPI 、Spring SPI、Dubbo SPI機制

        共 49030字,需瀏覽 99分鐘

         ·

        2021-08-06 06:10

        點擊上方藍色字體,選擇“標星公眾號”

        優(yōu)質文章,第一時間送達

        JDK SPI機制

        SPI(Service Provider Interface),是一種將服務接口與服務實現分離以達到解耦可拔插、大大提升了程序可擴展性的機制。

        約定(我覺得稱之為規(guī)范更合適):

        1. 制定統(tǒng)一的規(guī)范(比如 java.sql.Driver)

        2. 服務提供商提供這個規(guī)范具體的實現,在自己jar包的META-INF/services/目錄里創(chuàng)建一個以服務接口命名的文件,內容是實現類的全命名(比如:com.mysql.jdbc.Driver)。

        3. 平臺引入外部模塊的時候,就能通過該jar包META-INF/services/目錄下的配置文件找到該規(guī)范具體的實現類名,然后裝載實例化,完成該模塊的注入。

        這個機制最大的優(yōu)點就是無須在代碼里指定,進而避免了代碼污染,實現了模塊的可拔插。

         JDK SPI的一個典型案例就是 java.sql.Driver 了

         

         

         我們最熟悉的代碼

        // 加載驅動
        Class.forName("com.mysql.jdbc.Driver");
        // 獲取連接
        Connection connection = DriverManager.getConnection("url""user""password");

        我們進入DriverManager類,里面有個靜態(tài)代碼塊,這部分的內容會先執(zhí)行。

        static {
            loadInitialDrivers();
            println("JDBC DriverManager initialized");
        }

        加載并初始化驅動

        private static void loadInitialDrivers() {
            // ...

            // 如果驅動被打包作為服務提供者,則加載它。
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    // 1. load
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                    // 2. 獲取Loader的迭代器
                    Iterator<Driver> driversIterator = loadedDrivers.iterator();
                    try{
                        // 3. 調用next方法
                        while(driversIterator.hasNext()) {
                            driversIterator.next();
                        }
                    } catch(Throwable t) {
                    // Do nothing
                    }
                    return null;
                }
            });
            // ...
        }

        看 ServiceLoader#load方法

        public static <S> ServiceLoader<S> load(Class<S> service) {
            // 獲取類加載器
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            // load
            return ServiceLoader.load(service, cl);
        }

        public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){
            // new 一個 ServiceLoader,參數為服務接口Class和類加載器
            return new ServiceLoader<>(service, loader);
        }

        可以看出,上面主要是獲取類加載器并新建ServiceLoader的過程,沒有加載實現類的動作。現在看:ServiceLoader#iterator 方法

        public Iterator<S> iterator() {
            // 這里是個Iterator的匿名內部類,重寫了一些方法
            return new Iterator<S>() {

                // 已存在的提供者
                Iterator<Map.Entry<String,S>> knownProviders
                    = providers.entrySet().iterator();

                public boolean hasNext() {
                    // 先檢查緩存
                    if (knownProviders.hasNext())
                        return true;
                    // 緩存沒有,走 java.util.ServiceLoader.LazyIterator#hasNext 方法
                    return lookupIterator.hasNext();
                }

                public S next() {
                    // 同樣先檢查緩存
                    if (knownProviders.hasNext())
                        return knownProviders.next().getValue();
                    // 緩存沒有,走 java.util.ServiceLoader.LazyIterator#next 方法
                    return lookupIterator.next();
                }

                public void remove() {
                    throw new UnsupportedOperationException();
                }

            };
        }

        看 ServiceLoader.LazyIterator#hasNext 方法

        // java.util.ServiceLoader.LazyIterator#hasNext
        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    // 獲取全路徑:META-INF/services/java.sql.Driver
                    String fullName = PREFIX + service.getName();
                    // 加載資源
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            // 這里負責解析前面加載的配置信息
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                // 解析的返回值是一個 Iterator<String> 類型,其中的String代表文件里配置的實現類全限定名,比如:com.mysql.jdbc.Driver
                pending = parse(service, configs.nextElement());
            }
            // 把nextName初始化
            nextName = pending.next();
            return true;
        }

        看 ServiceLoader.LazyIterator#next 方法

        // java.util.ServiceLoader.LazyIterator#next
        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
        // 進入 java.util.ServiceLoader.LazyIterator#nextService 方法
        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            // com.mysql.jdbc.Driver
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                // 加載 com.mysql.jdbc.Driver Class
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                // 實例化并轉換成 com.mysql.jdbc.Driver 對象
                S p = service.cast(c.newInstance());
                // 添加到緩存
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

        所以,DriverManager做了什么事:

        // 1. 獲取類加載器并創(chuàng)建ServiceLoader對象
        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        // 2. 創(chuàng)建迭代器并覆蓋hasNext和next方法
        Iterator<Driver> driversIterator = loadedDrivers.iterator();

        try{
            // 3. 這個方法主要是讀取配置文件,獲取其中實現類的全限定名。
            while(driversIterator.hasNext()) {
                // 4. 這個方法主要是根據全限定名去生成一個實例
                driversIterator.next();
            }
        } catch(Throwable t) {
        // Do nothing
        }

        另外你也能發(fā)現,ServiceLoader只提供了遍歷的方式來獲取目標實現類,沒有提供按需加載的方法,這也是常說的不足之處。

        自己實現

        定義一個接口

        package com.demo.spi;

        public interface Funny {

            void deal();
        }

        寫一個實現類

        package com.demo.spi;

        public class FunnyImpl implements Funny {
            @Override
            public void deal() {
                System.out.println("FunnyImpl");
            }
        }

        配置

        文件內容

         執(zhí)行測試

        @Test
        public void test(){
            ServiceLoader<Funny> serviceLoader = ServiceLoader.load(Funny.class);
            serviceLoader.forEach(Funny::deal);
        }

        輸出

        Spring SPI機制

        對于Spring的SPI機制主要體現在SpringBoot中。我們知道SpringBoot的啟動包含new SpringApplication和執(zhí)行run方法兩個過程,new的時候有這么個邏輯:(getSpringFactoriesInstances)

        這個方法走到里面,無非 1. 加載類的全限定名列表。2. 根據類名通過反射實例化。

        重點在于:SpringFactoriesLoader.loadFactoryNames(type, classLoader) 

        public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            // 獲取類的全限定名
            String factoryClassName = factoryClass.getName();
            // 1. 執(zhí)行l(wèi)oadSpringFactories,這里只傳入了類加載器,肯定是要獲取全部配置
            // 2. getOrDefault,獲取指定接口的實現類名稱列表,如果沒有則返回一個空列表
            return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }

        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            // 先檢查緩存
            MultiValueMap<String, String> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }
            try {
                // 獲取類路徑下所有META-INF/spring.factories
                Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                result = new LinkedMultiValueMap<>();
                // 把加載的配置轉換成Map<String, List<String>>格式
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                        String factoryClassName = ((String) entry.getKey()).trim();
                        for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }

        它還提供了實例化的方法:SpringFactoriesLoader.loadFactories(factoryClass, classLoader) 利用反射實現,理解起來也不困難,不做解釋了

        對比ServiceLoader:

        1. 都是XXXLoader。命名格式都一樣。

        2. 一個是加載 META-INF/services/ 目錄下的配置;一個是加載 META-INF/spring.factories 固定文件的配置。思路都一樣。

        3. 兩個都是利用ClassLoader和ClassName來完成操作的。不同的是Java的ServiceLoader加載配置和實例化都是自己來實現,并且不能按需加載;SpringFactoriesLoader既可以單獨加載配置然后按需實例化也可以實例化全部。

        如何實現一個Spring-Boot-Starter?

        先看一下SpringBoot的相關要點。

         

        這個是SpringBoot的 @SpringBootApplication 注解,里面還有一個 @EnableAutoConfiguration 注解,開啟自動配置的。

         

        可以發(fā)現這個自動配置注解在另一個工程,而這個工程里也有個spring.factories文件,如下圖:

         

        我們知道在SpringBoot的目錄下有個spring.factories文件,里面都是一些接口的具體實現類,可以由SpringFactoriesLoader加載。

        那么同樣的道理,spring-boot-autoconfigure模塊也能動態(tài)加載了??匆幌缕渲械膬热荩?/span>

         

        我們看到有個接口【org.springframework.boot.autoconfigure.EnableAutoConfiguration】,有N個配置類,都是自動配置相關的,那么我們可以猜一猜,是不是模仿一下這樣的配置,我們就可以絲滑進入了?

        為了證明這一點,我們看一下 dubbo-spring-boot-starter 的結構

         

        你會發(fā)現starter項目沒有實現類,只有個pom文件。

         

        它的關鍵在于引入了一個 autoconfigure 依賴。

         

        這個里面配置了Spring的 spring.factories 其中只有一個配置

         

        看到這里你是不是感覺到了什么:

        1. 新建一個只有pom的starter工程,引入寫好的自動配置模塊。

        2. 自動配置模塊配置 spring.factories 文件,格式如上。

        3. 具體實現自動配置邏輯。

        這樣當SpringBoot啟動加載配置類的時候,就會把這些第三方的配置一起加載。

        所以我認為 Starter的作用就是在啟動之前把第三方的配置加載到容器中,具體表現就是無須用戶手動配置即可使用?!救绻粶蚀_望指正】

        具體實現一個starter

        兩個maven工程,目錄如下:

        custom-spring-boot-autoconfigure↓↓↓↓↓

        pom

        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.demo</groupId>
            <artifactId>custom-spring-boot-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>

            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-autoconfigure</artifactId>
                    <version>2.1.6.RELEASE</version>
                </dependency>
            </dependencies>

        </project>

        CustomAutoConfiguration

        import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;

        @Configuration
        @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
        public class CustomAutoConfiguration {

            @Bean
            public CustomConfig customConfig(){
                System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!第三方自定義的配置");
                return new CustomConfig();
            }
        }


        CustomConfig

        /**
         * 自定義配置
         */
        public class CustomConfig {

            private String value = "Default";
        }

        spring.factories

        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        com.demo.CustomAutoConfiguration

        一共四個文件

        custom-spring-boot-starter↓↓↓↓

        只有pom文件

        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.demo</groupId>
            <artifactId>custom-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>

            <dependencies>
                <dependency>
                    <groupId>com.demo</groupId>
                    <artifactId>custom-spring-boot-autoconfigure</artifactId>
                    <version>1.0-SNAPSHOT</version>
                </dependency>

            </dependencies>

        </project>

        最后把這兩個工程install

         

        下面是新建一個SpringBoot項目,然后引入咱們自定義的starter依賴。

        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>custom-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        然后啟動就可以啦,可以看見,用戶引入依賴之后無需手動配置就可以使用第三方插件

         

        看下工程依賴

        Dubbo SPI機制

        上面介紹了兩種SPI,到Dubbo這里就不難理解了吧?

        思路都是處理類+約定配置 ,在Dubbo里,約定擴展配置在  META-INF/dubbo/internal/ 目錄下

        在Dubbo源碼里也可以發(fā)現,核心類是 ExtensionLoader

        private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

        private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

        getExtensionLoader

        // 這個方法就是獲取一個特定類型的ExtensionLoader的實例
        public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
            if (type == null) {
                throw new IllegalArgumentException("Extension type == null");
            }
            if (!type.isInterface()) {
                throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
            }
            if (!withExtensionAnnotation(type)) {
                throw new IllegalArgumentException("Extension type (" + type +
                        ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
            }
            // 先從緩存中取
            ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
            if (loader == null) {
                // 緩存沒有則新建
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
                loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
            }
            return loader;
        }

        下面看getExtension方法

        public T getExtension(String name) {
            if (StringUtils.isEmpty(name)) {
                throw new IllegalArgumentException("Extension name == null");
            }
            if ("true".equals(name)) {
                // 獲取默認的擴展類
                return getDefaultExtension();
            }
            // Holder用來持有目標對象
            final Holder<Object> holder = getOrCreateHolder(name);
            // 獲取實例
            Object instance = holder.get();
            // 沒有的話,雙重檢查并創(chuàng)建實例
            if (instance == null) {
                synchronized (holder) {
                    instance = holder.get();
                    if (instance == null) {
                        instance = createExtension(name);
                        holder.set(instance);
                    }
                }
            }
            return (T) instance;
        }

        看createExtension方法

        private T createExtension(String name) {
            // 1. 獲取擴展Class
            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);
                }
                // 2. IOC注入依賴
                injectExtension(instance);
                Set<Class<?>> wrapperClasses = cachedWrapperClasses;
                if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                    for (Class<?> wrapperClass : wrapperClasses) {
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
                return instance;
            } catch (Throwable t) {
                throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                        type + ") couldn't be instantiated: " + t.getMessage(), t);
            }
        }


        1. 獲取所有擴展類

        private Map<String, Class<?>> getExtensionClasses() {
            // 緩存 + 雙重檢查
            Map<String, Class<?>> classes = cachedClasses.get();
            if (classes == null) {
                synchronized (cachedClasses) {
                    classes = cachedClasses.get();
                    if (classes == null) {
                        // 加載擴展類
                        classes = loadExtensionClasses();
                        cachedClasses.set(classes);
                    }
                }
            }
            return classes;
        }

        // 對于loadDirectory,每個目錄有兩個執(zhí)行邏輯,因為目前要兼容alibaba老版本
        private Map<String, Class<?>> loadExtensionClasses() {
            cacheDefaultExtensionName();
            Map<String, Class<?>> extensionClasses = new HashMap<>();
            // 加載目錄:META-INF/dubbo/internal/
            loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
            loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache""com.alibaba"));
            // 加載目錄:META-INF/dubbo/
            loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
            loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache""com.alibaba"));
            // 加載目錄:META-INF/services/
            loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
            loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache""com.alibaba"));
            return extensionClasses;
        }

        private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
            // 文件路徑,比如 META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
            String fileName = dir + type;
            try {
                Enumeration<java.net.URL> urls;
                ClassLoader classLoader = findClassLoader();
                // 加載文件
                if (classLoader != null) {
                    urls = classLoader.getResources(fileName);
                } else {
                    urls = ClassLoader.getSystemResources(fileName);
                }
                if (urls != null) {
                    while (urls.hasMoreElements()) {
                        java.net.URL resourceURL = urls.nextElement();
                        // 加載資源(讀取文件內容)
                        loadResource(extensionClasses, classLoader, resourceURL);
                    }
                }
            } catch (Throwable t) {
                logger.error("Exception occurred when loading extension class (interface: " +
                        type + ", description file: " + fileName + ").", t);
            }
        }

        private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
            try {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                    String line;
                    // 按行讀取
                    while ((line = reader.readLine()) != null) {
                        // 判斷注釋
                        final int ci = line.indexOf('#');
                        if (ci >= 0) {
                            // 截取 # 之前的字符串,# 之后的內容為注釋,需要忽略
                            line = line.substring(0, ci);
                        }
                        line = line.trim();
                        if (line.length() > 0) {
                            try {
                                String name = null;
                                // 按照 = 分割,前面是name,后面是類全限定名
                                int i = line.indexOf('=');
                                if (i > 0) {
                                    name = line.substring(0, i).trim();
                                    line = line.substring(i + 1).trim();
                                }
                                if (line.length() > 0) {
                                    // 加載Class
                                    loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                                }
                            } catch (Throwable t) {
                                IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                                exceptions.put(line, e);
                            }
                        }
                    }
                }
            } catch (Throwable t) {
                logger.error("Exception occurred when loading extension class (interface: " +
                        type + ", class file: " + resourceURL + ") in " + resourceURL, t);
            }
        }

        private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
            if (!type.isAssignableFrom(clazz)) {
                throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                        type + ", class line: " + clazz.getName() + "), class "
                        + clazz.getName() + " is not subtype of interface.");
            }
            // 檢測目標類上是否有 Adaptive 注解
            if (clazz.isAnnotationPresent(Adaptive.class)) {
                // 設置 cachedAdaptiveClass緩存
                cacheAdaptiveClass(clazz);
            // 檢測 clazz 是否是 Wrapper 類型
            } else if (isWrapperClass(clazz)) {
                // 存儲 clazz 到 cachedWrapperClasses 緩存中
                cacheWrapperClass(clazz);
            } else {
                // 檢測 clazz 是否有默認的構造方法,如果沒有,則拋出異常
                clazz.getConstructor();
                if (StringUtils.isEmpty(name)) {
                    // 如果 name 為空,則嘗試從 Extension 注解中獲取 name,或使用小寫的類名作為 name
                    name = findAnnotationName(clazz);
                    if (name.length() == 0) {
                        throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                    }
                }

                String[] names = NAME_SEPARATOR.split(name);
                if (ArrayUtils.isNotEmpty(names)) {
                    // 存儲 name 到 Activate 注解對象的映射關系
                    cacheActivateClass(clazz, names[0]);
                    for (String n : names) {
                        // 存儲 Class 到名稱的映射關系
                        cacheName(clazz, n);
                        // 存儲名稱到 Class 的映射關系
                        saveInExtensionClass(extensionClasses, clazz, n);
                    }
                }
            }
        }

        這部分有些注釋直接拿的官網,總結下來這部分:

        1. 根據約定的路徑比如【META-INF/dubbo/internal/】加載文件URl。

        2. 讀取并解析每一個文件內容,具體為按行讀取,去掉注釋,按等號分割為 name-class全限定名鍵值對。

        3. 根據類全限定名生成Class對象,根據Class對象的的特點進行相關的緩存以及name到Class對象的緩存。

        2. IOC注入依賴

        private T injectExtension(T instance) {
            try {
                if (objectFactory != null) {
                    for (Method method : instance.getClass().getMethods()) {
                        // 這里判斷方法是否是setter方法,Dubbo目前只處理setter的IOC
                        if (isSetter(method)) {
                            // 如果標注了@DisableInject,則不進行注入
                            if (method.getAnnotation(DisableInject.class) != null) {
                                continue;
                            }
                            // 獲取 setter 方法參數類型
                            Class<?> pt = method.getParameterTypes()[0];
                            // 基本類型跳過
                            if (ReflectUtils.isPrimitives(pt)) {
                                continue;
                            }
                            try {
                                // 獲取屬性名,比如 setName 方法對應屬性名 name
                                String property = getSetterProperty(method);
                                // 從 ObjectFactory 中獲取依賴對象
                                Object object = objectFactory.getExtension(pt, property);
                                if (object != null) {
                                    // 通過反射調用 setter 方法設置object依賴
                                    method.invoke(instance, object);
                                }
                            } catch (Exception e) {
                                logger.error("Failed to inject via method " + method.getName()
                                        + " of interface " + type.getName() + ": " + e.getMessage(), e);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            return instance;
        }

        這部分容易理解,核心部分利用反射實現,其它前置處理做了一些校驗。

        而使用Dubbo SPI的話也是一樣的套路,擴展類實現 + 配置 + ExtensionLoader


          作者 |  露娜妹

        來源 |  cnblogs.com/LUA123/p/12460869.html

        瀏覽 129
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 4080yy午夜理论片成人 | 午夜精品视频一区 | 婷婷亚洲视频 | 欧美777精品久久久久网 | 亚洲AV色影视 |