Spring-Boot-Dubbo 简单案例
Spring Boot 整合 Dubbo 版本说明:
spring-boot 2.3.0.RELEASE
dubbo 2.7.8
Dubbo 更多功能的 资料地址
什么是 SPI 机制 ?
SPI 机制
在双亲委托模型下,类加载是由下而上的,即下层的类加载器会委托上层进行加载。Java 为了实现可拓展性在核心库中只定义了接口信息,而这些接口的实现可以由不同的三方厂家实现。但是 Java 的启动类加载器不会加载其它来源的 jar 包,这样就无法满足 Java 的拓展性需求。
针对这一个问题 Java 提供了线程上下文类加载器来解决这个问题, 他可以让父 ClassLoader 可以使用当前线程Thread.currentThread().getContextLoader(). 所指定的 ClassLoader 加载的类。这就改变了父 ClassLoader 不能使用 ClassLoader 或是没有直接父子关系的 ClassLoader 加载的类的情况,即改变了双亲委托模型。
这个整个打破双亲委派模型的类加载机制,就是 Java 的 SPI(Service Provider Interface) 机制。它有很多我们熟知的案例 JDBC
、Spring-Boot
、Dubbo
ServiceLoader 核心原理
这个整个过程
Java 中 SPI 的使用
- 定义接口和实现类
// 接口类 public interface Car { void start(); } // 实现类 public class RedCar implements Car { private String color = "red"; @Override public void start() { System.out.println(this.color + " car start!"); } }
- 定义 META-INF/services/ cn.spi.Car 文件文件内容
cn.spi.RedCar
- 通过 SPI 获取实例对象
ServiceLoader<Car> cars = ServiceLoader.load(Car.class); for (Car car : cars) { car.start(); }
- 获取代理类
Dubbo 中的 SPI
在 Dubbo 中 SPI 的流程是这样的首先 ExtensionLoader
是 Dubbo
SPI 实现的核心类,它下面有连个核心方法 getExtension
和 createExtension
。在 createExtension
中完成对象的创建,在这个创建的过程中一共有以下几个步骤:
- 读取配置文件通过 name 参数找到实现类;
- 对实现类进行实例化;
- 依赖注入;
- AOP;
- 返回对象实例。
一. Dubbo 中的 SPI 实现
使用步骤
还是使用我们之前 Car 接口,下面是咱们有变化的部分
- 定义配置文件 META-INF/dubbo/ cn.spi.Car 文件内容
red=cn.spi.RedCar
- 获取对象实例
ExtensionLoader<Car> extensionLoader = ExtensionLoader .getExtensionLoader(Car.class); Car car = extensionLoader.getExtension("red");
- 依赖注入实现代理类
// 定义 @SPI public interface Car { void start(); /** * 方法被代理需要两个步骤: * 1. 增加 @Adaptive 注解 * 2. URL 参数, * * @param url 服务提供者的 URL * @return */ @Adaptive String color(URL url); } // 使用 ExtensionLoader<Person> extensionLoader2 = ExtensionLoader.getExtensionLoader(Person.class); Person person = extensionLoader2.getExtension("black"); URL url = new URL("http", "localhost", 8080); //代理逻辑 url = url.addParameter("car", "red"); person.getCar().color(url);
源码分析
getExtension
方法源码分析
// 获取实例 public T getExtension(String name, boolean wrap) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { return getDefaultExtension(); } final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); if (instance == null) { // DCL synchronized (holder) { instance = holder.get(); if (instance == null) { // 创建对象 instance = createExtension(name, wrap); holder.set(instance); } } } return (T) instance; } // 创建实例 private T createExtension(String name, boolean wrap) { Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // .... ioc, aop return instance; } // 读取类 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 Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap<>(); // LoadingStrategy 策略接口,读取 SPI 目录下的类 for (LoadingStrategy strategy : strategies) { loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); } return extensionClasses; } // loadDirectory 读取目录查找文件 // loadResource 读取文件 // loadClass 加载类 // saveInExtensionClass 保存类信息到 Map