Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker DevOps


Java SPI 机制

Java 大约 3278 字

示例代码

接口

package com.example.service;

public interface HelloService {

    void sayHello();

}

实现类

package com.example.impl;

import com.example.service.HelloService;

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("hello from impl");
    }
}

配置文件

META-INF文件夹下新建以接口全限定名为名称的文件,文件内容是具体实现类的全限定名。

└─src
    ├─com
    │  └─example
    │      │  Test.java
    │      │
    │      ├─impl
    │      │      HelloServiceImpl.java
    │      │      HelloServiceImpl2.java
    │      │
    │      └─service
    │              HelloService.java
    │
    └─META-INF
        └─services
                com.example.service.HelloService

示例文件内容:

com.example.impl.HelloServiceImpl
com.example.impl.HelloServiceImpl2

SPI

public class Test {

    public static void main(String[] args) {
        ServiceLoader<HelloService> serviceLoader = ServiceLoader.load(HelloService.class);
        // 增强 for 循环编译成字节码后就是迭代器
        // for (HelloService service : serviceLoader) {
        //     service.sayHello();
        // }

        Iterator<HelloService> iterator = serviceLoader.iterator();
        while (iterator.hasNext()) {
            HelloService helloService = iterator.next();
            helloService.sayHello();
        }
    }

}

输出:

hello from impl
hello from impl2

原理

LazyIterator中的hasNextService判断fullNamenextService中使用Class.forName()加载。

public final class ServiceLoader<S> implements Iterable<S> {

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

    public Iterator<S> iterator() {
        return new Iterator<S>() {

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

        };
    }

    private class LazyIterator implements Iterator<S> {

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                String fullName = PREFIX + service.getName();
                if (loader == null)
                    configs = ClassLoader.getSystemResources(fullName);
                else
                    configs = loader.getResources(fullName);
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;

            c = Class.forName(cn, false, loader);

            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;

            throw new Error();          // This cannot happen
        }

        public boolean hasNext() {
            return hasNextService();
        }

        public S next() {
            return nextService();
        }

    }

}
阅读 373 · 发布于 2022-04-09

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批