1. SPI机制

SPI 全称为 Service Provider Interface,是一种服务发现机制。

SPI 的本质是将接口实现类全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。

2. Java SPI

2.1 demo

首先,定义接口;

public interface DataStorage {

String search(String key);
}

其次,定义两个对该接口的实现类;

// 1. msyql存储实现类
public class MysqlStorage implements DataStorage {

@Override
public String search(String key) {
return "【Mysql】搜索" + key + ",结果:No";
}

}

// 2. redis存储实现类
public class RedisStorage implements DataStorage {
@Override
public String search(String key) {
return "【Redis】搜索" + key + ",结果:Yes";
}
}

接下来 META-INF/services 文件夹下创建一个文件,名称为 DataStorage 的全限定名com.smy.spi.DataStorage。文件内容为实现类的全限定的类名,如下:

com.smy.spi.MysqlStorage
com.smy.spi.RedisStorage

最后,使用ServiceLoader加载。

public class SpiDemo {
public static void main(String[] args) {

ServiceLoader<DataStorage> serviceLoader = ServiceLoader.load(DataStorage.class);
System.out.println("============ Java SPI 测试============");
// 1. forEach模式
serviceLoader.forEach(loader -> System.out.println(loader.search("Yes Or No")));
// 2. 迭代器模式
Iterator<Robot> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
DataStorage dataStorage = iterator.next();
robot.search();
}
}

}

2.2 原理

ServiceLoader的load方法

public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}

public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}

创建一个 ServiceLoader 对象

private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}

// 缓存已经被实例化的服务提供者,按照实例化的顺序存储
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}

LazyIterator

private class LazyIterator implements Iterator<S>
{

Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;

private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}

// 1. hasNextService
// 通过加载器通过文件全名获取配置对象 Enumeration<URL> configs ,然后调用解析parse方法解析classpath下的META-INF/services/目录里以服务接口命名的文件。
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// PREFIX = "META-INF/services/";
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;
}
// 解析配置文件
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}

// 2. nextService
// 反射方法 Class.forName() 加载类对象,并用newInstance方法将类实例化,并把实例化后的类缓存到providers对象中,(LinkedHashMap<String,S>类型,然后返回实例对象
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
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 {
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
}

// 3. 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);
}
}

// 4. 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);
}
}

// 5. remove
public void remove() {
throw new UnsupportedOperationException();
}

}

2.3 缺陷

  1. 不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。
  2. 获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。
  3. 多个并发多线程使用 ServiceLoader 类的实例是不安全的。

2.4 JDBC DriverManager

DriverManager加载驱动流程:

  1. 系统变量中获取有关驱动的定义。

  2. 使用 SPI 来获取驱动的实现类(字符串的形式)。

  3. 遍历使用 SPI 获取到的具体实现,实例化各个实现类。

  4. 根据第一步获取到的驱动列表来实例化具体实现类。

首先在JDBC4.1之前,如果需要连接数据,需要先加载数据库相关的驱动,然后在进行数据库的连接操作;

 //STEP1 : Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//STEP2 :Open a connection
String url = "jdbc:xxxx://xxxx:xxxx/xxxx";
Connection conn = DriverManager.getConnection(url,username,password);

其次,在JDBC4.1之后使用了Java的SPI机制,可以直接使用JDBC驱动。

/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}

在 Java 中,static 块用于静态初始化,它在类被加载到 Java 虚拟机中时执行。静态块会加载实例化驱动,接下来我们看看 loadInitialDrivers 方法。

private static void loadInitialDrivers() {
String drivers;
// 1. 从系统变量中获取有关驱动的定义
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// 2. 使用SPI来获取驱动的实现
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 3. 遍历使用SPI获取到的具体实现类,实例化实现类
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
// 4. 遍历使用SPI获取到的具体实现,实例化各个实现类
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});

println("DriverManager.initialize: jdbc.drivers = " + drivers);

if (drivers == null || drivers.equals("")) {
return;
}
// 4. 根据第一步获取到的驱动列表来实例化具体的实现类
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}

3. Spring SPI

3.1 demo

Spring SPI 沿用了 Java SPI 的设计思想,Spring 采用的是 spring.factories 方式实现 SPI 机制,可以在不修改 Spring 源码的前提下,提供 Spring 框架的扩展性。

  • 创建 MyTestService 接口

    public interface MyTestService{

    void printMylife();

    }
  • 创建 MyTestService 接口实现类

    1. WorkTestService

      public class WorkTestService implements MyTestService{

      public WorkTestService(){

      System.out.println("WorkTestService");

      }

      public void printMylife(){

      System.out.println("我的工作");

      }

      }
    2. FamilyTestService

      public class FamilyTestService implements MyTestService{

      public FamilyTestService(){

      System.out.println("FamilyTestService");

      }

      public void printMylife(){

      System.out.println("我的家庭");

      }

      }
  • 在资源文件目录,创建一个固定的文件 META-INF/spring.factories。

    #key是接口的全限定名,value是接口的实现类

    com.courage.platform.sms.demo.service.MyTestService = com.courage.platform.sms.demo.service.impl.FamilyTestService,com.courage.platform.sms.demo.service.impl.WorkTestService
  • 运行代码

      //调用SpringFactoriesLoader.loadFactories方法加载MyTestService接口所有实现类的实例
    List<MyTestService> myTestServices = SpringFactoriesLoader.loadFactories(MyTestService.class,
    Thread.currentThread().getContextClassLoader()
    );

    for(MyTestService testService : myTestServices){
    testService.printMylife();
    }

    输出结果:
    FamilyTestService
    WorkTestService
    我的家庭
    我的工作

    Spring SPI 机制非常类似 ,但还是有一些差异:

    1. Java SPI 是一个服务提供接口对应一个配置文件,配置文件中存放当前接口的所有实现类,多个服务提供接口对应多个配置文件,所有配置都在 services 目录下。
    2. Spring SPI 是一个 spring.factories 配置文件存放多个接口及对应的实现类,以接口全限定名作为key,实现类作为value来配置,多个实现类用逗号隔开,仅 spring.factories 一个配置文件。

    和 Java SPI 一样,**Spring SPI 也无法获取某个固定的实现,只能按顺序获取所有实现**。

    ## 4. Dubbo SPI

    ### 4.2 demo

    基于 Java SPI 的缺陷无法支持按需加载接口实现类,Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。

    Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。

    Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下:

    ```java
    optimusPrime = org.apache.spi.OptimusPrime
    bumblebee = org.apache.spi.Bumblebee

与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。

另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。

下面来演示 Dubbo SPI 的用法:

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();

}
}

5. 自定义类加载器

5.1 扩展类加载器

public class ExtensionLoader<T> {

private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);

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

private static final String SMS_DIRECTORY = "META-INF/sms/";

private static final String DEFAULT_CLASSLOADER_POLICY = "internal";

private static final String WORKER_DIR_NAME = "platform-sms-admin";

private static final Pattern NAME_SEPARATOR = Pattern
.compile("\\s*[,]+\\s*");

private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();

private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();

private static final ConcurrentMap<String, Object> EXTENSION_KEY_INSTANCE = new ConcurrentHashMap<>();

private final Class<?> type;

private final String classLoaderPolicy;

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();

private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

private String cachedDefaultName;

private ConcurrentHashMap<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();

private static <T> boolean withExtensionAnnotation(Class<T> type) {
return type.isAnnotationPresent(SPI.class);
}

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
return getExtensionLoader(type, DEFAULT_CLASSLOADER_POLICY);
}

@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type, String classLoaderPolicy) {
if (type == null) throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @"
+ SPI.class.getSimpleName() + " Annotation!");
}

ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type, classLoaderPolicy));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}

private ExtensionLoader(Class<?> type, String classLoaderPolicy) {
this.type = type;
this.classLoaderPolicy = classLoaderPolicy;
}

/**
* 返回指定名字的扩展
*
* @param name
* @return
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<>());
holder = cachedInstances.get(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;
}

public T getExtensionDirectly(String name) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Extension name == null");
}
return createExtensionDirectly(name);
}

@SuppressWarnings("unchecked")
public T getExtension(String name, String key) {
if (name == null || name.length() == 0) throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
String extKey = name + "-" + StringUtils.trimToEmpty(key);
Holder<Object> holder = cachedInstances.get(extKey);
if (holder == null) {
cachedInstances.putIfAbsent(extKey, new Holder<>());
holder = cachedInstances.get(extKey);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name, key);
holder.set(instance);
}
}
}
return (T) instance;
}

/**
* 返回缺省的扩展,如果没有设置则返回<code>null</code>
*/
public T getDefaultExtension() {
getExtensionClasses();
if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) {
return null;
}
return getExtension(cachedDefaultName);
}

// public private method 分界线

@SuppressWarnings("unchecked")
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
+ ") could not be instantiated: class could not be found");
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
+ ") could not be instantiated: " + t.getMessage(),
t);
}
}

@SuppressWarnings("unchecked")
private T createExtension(String name, String key) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
+ ") could not be instantiated: class could not be found");
}
try {
T instance = (T) EXTENSION_KEY_INSTANCE.get(name + "-" + key);
if (instance == null) {
EXTENSION_KEY_INSTANCE.putIfAbsent(name + "-" + key, clazz.newInstance());
instance = (T) EXTENSION_KEY_INSTANCE.get(name + "-" + key);
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
+ ") could not be instantiated: " + t.getMessage(),
t);
}
}

private T createExtensionDirectly(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
+ ") could not be instantiated: class could not be found");
}
try {
T instance = (T) clazz.newInstance();
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
+ ") could not be instantiated: " + t.getMessage(),
t);
}
}

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;
}

private String getJarDirectoryPath() {
String userDir = System.getProperty("user.dir");
URL url = Thread.currentThread().getContextClassLoader().getResource("");
String dirtyPath;
if (url != null) {
dirtyPath = url.toString();
} else {
File file = new File("");
dirtyPath = file.getAbsolutePath();
}
String jarPath = dirtyPath.replaceAll("^.*file:/", ""); // removes
// file:/ and
// everything
// before it
jarPath = jarPath.replaceAll("jar!.*", "jar"); // removes everything
// after .jar, if .jar
// exists in dirtyPath
jarPath = jarPath.replaceAll("%20", " "); // necessary if path has
// spaces within
if (!jarPath.endsWith(".jar")) { // this is needed if you plan to run
// the app using Spring Tools Suit play
// button.
jarPath = jarPath.replaceAll("/classes/.*", "/classes/");
}
logger.info("jarPath:" + jarPath);
Path path = Paths.get(jarPath).getParent(); // Paths - from java 8
if (path != null) {
logger.info("getJarDirectoryPath:" + path);
return path.toString();
}
return null;
}

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 jarDirectoryPath = this.getJarDirectoryPath();
// compatible with jdk17
if (jarDirectoryPath.endsWith("lib") || jarDirectoryPath.endsWith("bin")) {
jarDirectoryPath = jarDirectoryPath.replaceAll("lib", "").replace("bin", "");
}

// 1. plugin folder,customized extension classLoader (jar_dir/plugin)
String dir = File.separator + jarDirectoryPath + File.separator + "plugin";

logger.info("dir:" + dir);

File externalLibDir = new File(dir);
if (!externalLibDir.exists()) {
externalLibDir = new File(File.separator + jarDirectoryPath + File.separator + WORKER_DIR_NAME
+ File.separator + "plugin");
}
logger.info("extension classpath dir: " + externalLibDir.getAbsolutePath());
if (externalLibDir.exists()) {
File[] files = externalLibDir.listFiles((dir1, name) -> name.endsWith(".jar"));
if (files != null) {
for (File f : files) {
URL url;
try {
url = f.toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException("load extension jar failed!", e);
}

ClassLoader parent = Thread.currentThread().getContextClassLoader();
URLClassLoader localClassLoader;
if (classLoaderPolicy == null || "".equals(classLoaderPolicy)
|| DEFAULT_CLASSLOADER_POLICY.equalsIgnoreCase(classLoaderPolicy)) {
localClassLoader = new URLClassExtensionLoader(new URL[]{url});
} else {
localClassLoader = new URLClassLoader(new URL[]{url}, parent);
}

loadFile(extensionClasses, SMS_DIRECTORY, localClassLoader);
loadFile(extensionClasses, SERVICES_DIRECTORY, localClassLoader);
}
}
}
// 只加载外部spi, 不加载classpath
// 2. load inner extension class with default classLoader
// ClassLoader classLoader = findClassLoader();
// loadFile(extensionClasses, SMS_DIRECTORY, classLoader);
// loadFile(extensionClasses, SERVICES_DIRECTORY, classLoader);

return extensionClasses;
}

private void loadFile(Map<String, Class<?>> extensionClasses, String dir, ClassLoader classLoader) {
String fileName = dir + type.getName();
try {
Enumeration<URL> urls;
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.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;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
Class<?> clazz = classLoader.loadClass(line);
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException(
"Error when load extension class(interface: " + type
+ ", class line: " + clazz.getName()
+ "), class " + clazz.getName()
+ "is not subtype of interface.");
} else {
try {
clazz.getConstructor(type);
} catch (NoSuchMethodException e) {
clazz.getConstructor();
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
cachedNames.remove(clazz);
throw new IllegalStateException(
"Duplicate extension " + type.getName() + " name "
+ n + " on "
+ c.getName() + " and "
+ clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException(
"Failed to load extension class(interface: " + type + ", class line: "
+ line + ") in " + url
+ ", cause: "
+ t.getMessage(),
t);
exceptions.put(line, e);
}
}
} // end of while read lines
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " + type + ", class file: " + url
+ ") in " + url,
t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error(
"Exception when load extension class(interface: " + type + ", description file: " + fileName + ").",
t);
}
}

@SuppressWarnings("unused")
private static ClassLoader findClassLoader() {
return ExtensionLoader.class.getClassLoader();
}

@Override
public String toString() {
return this.getClass().getName() + "[" + type.getName() + "]";
}

private static class Holder<T> {

private volatile T value;

private void set(T value) {
this.value = value;
}

private T get() {
return value;
}

}
}

5.2 使用

  • 第一步:创建静态加载器 ExtensionLoader

    private static ExtensionLoader<OuterAdapter> EXTENSION_LOADER = ExtensionLoader.getExtensionLoader(OuterAdapter.class);
  • 第二步:通过加载器生成适配器对象

    OuterAdapter adapter = EXTENSION_LOADER.getExtensionDirectly(adapterName);
@Component
public class AdapterLoader {

private final static Logger logger = LoggerFactory.getLogger(AdapterLoader.class);

// 第一步
private static ExtensionLoader<OuterAdapter> EXTENSION_LOADER = ExtensionLoader.getExtensionLoader(OuterAdapter.class);

private static ConcurrentHashMap<Integer, OuterAdapter> ADAPTER_MAP = new ConcurrentHashMap<>(16);

public void loadAdapter(SmsChannelConfig smsChannelConfig) {
String adapterName = smsChannelConfig.getChannelType();
try {
// 第二步
// 直接通过 Class 创建实例对象
OuterAdapter adapter = EXTENSION_LOADER.getExtensionDirectly(adapterName);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// 替换 ClassLoader
Thread.currentThread().setContextClassLoader(adapter.getClass().getClassLoader());
adapter.init(smsChannelConfig);
Thread.currentThread().setContextClassLoader(cl);
// 卸载之前的适配器
OuterAdapter preAdapter = ADAPTER_MAP.get(smsChannelConfig.getId());
if (preAdapter != null) {
preAdapter.destroy();
}
// 加入到容器里
ADAPTER_MAP.put(smsChannelConfig.getId(), adapter);
logger.info("Load sms adapter: {} succeed", adapterName);
} catch (Exception e) {
logger.error("Load sms adapter: {} failed", adapterName, e);
}
}

public OuterAdapter getAdapterByChannelId(Integer id) {
return ADAPTER_MAP.get(id);
}

}

本站由 卡卡龙 使用 Stellar 1.33.1主题创建

本站访问量 次. 本文阅读量 次.