zoukankan      html  css  js  c++  java
  • JDK SPI 、Spring SPI、Dubbo SPI机制

    JDK SPI机制

     SPI(Service Provider Interface),是一种将服务接口与服务实现分离以达到解耦可拔插、大大提升了程序可扩展性的机制。

    约定(我觉得称之为规范更合适):

    1. 制定统一的规范(比如 java.sql.Driver

    2. 服务提供商提供这个规范具体的实现,在自己jar包的META-INF/services/目录里创建一个以服务接口命名的文件,内容是实现类的全命名(比如:com.mysql.jdbc.Driver)。

    3. 平台引入外部模块的时候,就能通过该jar包META-INF/services/目录下的配置文件找到该规范具体的实现类名,然后装载实例化,完成该模块的注入。

    这个机制最大的优点就是无须在代码里指定,进而避免了代码污染,实现了模块的可拔插。

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

     

     我们最熟悉的代码

    // 加载驱动
    Class.forName("com.mysql.jdbc.Driver");
    // 获取连接
    Connection connection = DriverManager.getConnection("url", "user", "password");

    我们进入DriverManager类,里面有个静态代码块,这部分的内容会先执行。

    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. 获取类加载器并创建ServiceLoader对象
    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
    // 2. 创建迭代器并覆盖hasNext和next方法
    Iterator<Driver> driversIterator = loadedDrivers.iterator();
    
    try{
        // 3. 这个方法主要是读取配置文件,获取其中实现类的全限定名。
        while(driversIterator.hasNext()) {
            // 4. 这个方法主要是根据全限定名去生成一个实例
            driversIterator.next();
        }
    } catch(Throwable t) {
    // Do nothing
    }

    另外你也能发现,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");
        }
    }

    配置

    文件内容

     执行测试

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

    输出

    Spring SPI机制

    对于Spring的SPI机制主要体现在SpringBoot中。我们知道SpringBoot的启动包含new SpringApplication执行run方法两个过程,new的时候有这么个逻辑:(getSpringFactoriesInstances

    这个方法走到里面,无非 1. 加载类的全限定名列表。2. 根据类名通过反射实例化。

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

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        // 获取类的全限定名
        String factoryClassName = factoryClass.getName();
        // 1. 执行loadSpringFactories,这里只传入了类加载器,肯定是要获取全部配置
        // 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 注解,开启自动配置的。

    可以发现这个自动配置注解在另一个工程,而这个工程里也有个spring.factories文件,如下图:

    我们知道在SpringBoot的目录下有个spring.factories文件,里面都是一些接口的具体实现类,可以由SpringFactoriesLoader加载。

    那么同样的道理,spring-boot-autoconfigure模块也能动态加载了。看一下其中的内容:

    我们看到有个接口【org.springframework.boot.autoconfigure.EnableAutoConfiguration】,有N个配置类,都是自动配置相关的,那么我们可以猜一猜,是不是模仿一下这样的配置,我们就可以丝滑进入了?

    为了证明这一点,我们看一下 dubbo-spring-boot-starter 的结构

    你会发现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源码里也可以发现,核心类是 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();
        // 没有的话,双重检查并创建实例
        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,每个目录有两个执行逻辑,因为目前要兼容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

  • 相关阅读:
    【机器学习】matplotlib库练习-函数绘图
    【算法】二分查找应用:直接在结果域中进行二分查找
    【机器学习】朴素贝叶斯-02
    【机器学习】朴素贝叶斯-01
    【机器学习】决策树-02
    【机器学习】决策树-01
    【机器学*】k*邻算法-03
    【机器学*】k*邻算法-02
    【LeetCode】剑指 Offer 04. 二维数组中的查找
    【感悟】观《BBC彩色二战纪录片》有感
  • 原文地址:https://www.cnblogs.com/LUA123/p/12460869.html
Copyright © 2011-2022 走看看