一文讲清楚DUBBO SPI机制六个特性
共 21830字,需浏览 44分钟
·
2022-01-21 05:05
JAVA前线
欢迎大家关注公众号「JAVA前线」查看更多精彩分享,主要内容包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时也非常欢迎大家加我微信「java_front」一起交流学习
1 文章概述
SPI(Service Provider Interface)是一种服务发现机制,本质是将接口实现类全限定名配置在文件,并由服务加载器读取配置文件加载实现类,这样可以在运行时动态为接口替换实现类,通过SPI机制可以为程序提供拓展功能。
我们之前在文章《JDK SPI机制》已经讨论了JDK SPI如何实现,在文章《SLF4J源码角度分析阿里开发手册日志规约》已经讨论了JDK SPI如何应用,本文我们分析DUBBO SPI机制,相较于JDK SPI至少进行了以下功能扩展:
指定具体扩展点 指定默认扩展点 类级别自适应扩展点 方法级自适应扩展点 自实现IOC 自实现AOP
2 指定具体扩展点
2.1 代码实例
第一步定义订单接口与订单模型
package com.java.front.dubbo.spi.api.order;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface OrderSpiService {
public boolean createOrder(OrderModel order);
}
public class OrderModel {
private String userId;
private String orderId;
}
第二步实现订单服务
package com.java.front.dubbo.spi.impl.order;
public class OrderSpiAServiceImpl implements OrderSpiService {
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiAService createOrder");
return Boolean.TRUE;
}
}
public class OrderSpiBServiceImpl implements OrderSpiService {
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiBService createOrder");
return Boolean.TRUE;
}
}
第三步新增配置文件
├─src
│ ├─main
│ │ └─resources
│ │ └─META-INF
│ │ ├─services
│ │ │ com.java.front.dubbo.spi.api.order.OrderSpiService
第四步新增配置文件内容
orderSpiAService=com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
orderSpiBService=com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
第五步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test1();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getExtension("orderSpiAService");
orderSpiService.createOrder(new OrderModel());
}
}
第六步输出测试结果
OrderSpiAService createOrder
2.2 源码分析
public class ExtensionLoader<T> {
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
private Map> loadExtensionClasses() {
// 省略代码
// KEY表示自定义名称、Value表示具体实现类
// orderSpiAService=class com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
// orderSpiBService=class com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
Map> extensionClasses = new HashMap>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
private void loadDirectory(Map> extensionClasses, String dir, String type) {
String fileName = dir + type;
try {
Enumeration 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 when load extension class(interface: " + type + ", description file: " + fileName + ").", t);
}
}
private void loadResource(Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
// 读取文件每一行
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) {
// 加载实现类
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);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
}
classes.get(name)根据输入名称获取扩展点
public class ExtensionLoader<T> {
private T createExtension(String name) {
// 根据name获取对应实现类
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);
}
// 省略代码
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t);
}
return instance;
}
}
3 指定默认扩展点
3.1 代码实例
第一步修改订单接口
package com.java.front.dubbo.spi.api.order;
import org.apache.dubbo.common.extension.SPI;
@SPI("orderSpiBService")
public interface OrderSpiService {
public boolean createOrder(OrderModel order);
}
第二步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test2();
}
public static void test2() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getDefaultExtension();
orderSpiService.createOrder(new OrderModel());
}
}
第三步输出测试结果
OrderSpiBService createOrder
3.2 源码分析
源码流程如下图
getDefaultExtension方法获取默认扩展点
public class ExtensionLoader<T> {
public T getDefaultExtension() {
// 加载实现类并设置cachedDefaultName
getExtensionClasses();
if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) {
return null;
}
// 根据默认名称获取扩展点
return getExtension(cachedDefaultName);
}
}
loadExtensionClasses方法设置默认扩展点名
public class ExtensionLoader<T> {
private Map> loadExtensionClasses() {
// 一个接口只允许有一个默认扩展点
// @SPI("orderSpiBService")表示设置orderSpiBService作为默认扩展点
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];
}
}
}
// KEY表示自定义名称、Value表示具体实现类
// orderSpiAService=class com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
// orderSpiBService=class com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
Map> extensionClasses = new HashMap>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
}
4 类级别自适应扩展点
4.1 代码实例
第一步新增订单自适应实现类
package com.java.front.dubbo.spi.impl.order;
import org.apache.dubbo.common.extension.Adaptive;
@Adaptive
public class OrderSpiAdaptiveServiceImpl implements OrderSpiService {
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiAdaptiveService createOrder");
return Boolean.TRUE;
}
}
第二步配置文件新增
orderAdaptiveService=com.java.front.dubbo.spi.impl.order.OrderSpiAdaptiveServiceImpl
第三步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test3();
}
public static void test3() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getAdaptiveExtension();
orderSpiService.createOrder(new OrderModel());
}
}
第四步输出测试结果
OrderSpiAdaptiveService createOrder
4.2 源码分析
源码流程如下图
getAdaptiveExtensionClass方法判断是否存在类级别自适应扩展点,如果存在则直接返回
public class ExtensionLoader<T> {
private Class> getAdaptiveExtensionClass() {
// 加载扩展点并设置cachedAdaptiveClass
getExtensionClasses();
// 存在类级别自适应扩展点则直接返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 不存在类级别自适应扩展点则动态创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
}
loadClass方法发现存在类级别自适应扩展点则设置cachedAdaptiveClass
public class ExtensionLoader<T> {
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class> clazz, String name) throws NoSuchMethodException {
// clazz是否为接口实现类
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.");
}
// 存在类级别自适应扩展点则设置cachedAdaptiveClass
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
}
// 一个接口只允许有一个类级别自适应扩展点
else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
}
}
// 省略代码
}
}
5 方法级自适应扩展点
5.1 代码实例
第一步新建库存接口与库存实体,使用方法级别自适应扩展点需要满足以下任意一个条件:
方法必须包含URL输入参数 方法必须包含get方法且返回值为URL
package com.java.front.dubbo.spi.api.stock;
import org.apache.dubbo.common.URL;
@SPI("stockA")
public interface StockSpiService {
@Adaptive("bizType")
public boolean reduceStock(String skuId, URL url);
@Adaptive("bizType")
public boolean reduceStock2(StockReduceModel stockReduceModel);
}
public class StockReduceModel {
private String skuId;
private URL url;
public StockReduceModel(String skuId, URL url) {
this.skuId = skuId;
this.url = url;
}
public String getSkuId() {
return skuId;
}
public void setSkuId(String skuId) {
this.skuId = skuId;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
}
第二步实现库存服务
package com.java.front.dubbo.spi.impl.stock;
import org.apache.dubbo.common.URL;
public class StockSpiAServiceImpl implements StockSpiService {
@Override
public boolean reduceStock1(String skuId, URL url) {
System.out.println("StockSpiAService reduceStock1 skuId=" + skuId);
return Boolean.TRUE;
}
@Override
public boolean reduceStock2(StockReduceModel stockReduceModel) {
System.out.println("StockSpiAService reduceStock2 stockReduceModel=" + stockReduceModel);
return Boolean.TRUE;
}
}
public class StockSpiBServiceImpl implements StockSpiService {
@Override
public boolean reduceStock1(String skuId, URL url) {
System.out.println("StockSpiBService reduceStock1 skuId=" + skuId);
return Boolean.TRUE;
}
@Override
public boolean reduceStock2(StockReduceModel stockReduceModel) {
System.out.println("StockSpiBService reduceStock2 stockReduceModel=" + stockReduceModel);
return Boolean.TRUE;
}
}
第三步新增配置文件
├─src
│ ├─main
│ │ └─resources
│ │ └─META-INF
│ │ ├─services
│ │ │ com.java.front.dubbo.spi.api.stock.StockSpiService
第四步新增配置文件内容
stockA=com.java.front.dubbo.spi.impl.stock.StockSpiAServiceImpl
stockB=com.java.front.dubbo.spi.impl.stock.StockSpiBServiceImpl
第五步运行测试代码
public class StockSpiServiceTest {
public static void main(String[] args) {
test1();
test2();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(StockSpiService.class);
StockSpiService adaptiveService = extensionLoader.getAdaptiveExtension();
Map map = new HashMap<>();
map.put("bizType", "stockB");
URL url = new URL(StringUtils.EMPTY, StringUtils.EMPTY, 1, map);
adaptiveService.reduceStock1("skuId_111", url);
}
public static void test2() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(StockSpiService.class);
StockSpiService adaptiveService = extensionLoader.getAdaptiveExtension();
Map map = new HashMap<>();
map.put("bizType", "stockB");
URL url = new URL(StringUtils.EMPTY, StringUtils.EMPTY, 1, map);
StockReduceModel stockReduceModel = new StockReduceModel("skuId_111", url);
adaptiveService.reduceStock2(stockReduceModel);
}
}
第六步输出测试结果
StockSpiBService reduceStock1 skuId=skuId_111
StockSpiBService reduceStock2 stockReduceModel=StockReduceModel(skuId=skuId_111, url=?bizType=stockB)
5.2 源码分析
源码流程如下图
getAdaptiveExtensionClass方法判断是否存在类级别自适应扩展点,如果不存在则动态创建
public class ExtensionLoader<T> {
private Class> getAdaptiveExtensionClass() {
// 加载扩展点并设置cachedAdaptiveClass
getExtensionClasses();
// 存在类级别自适应扩展点则直接返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 不存在类级别自适应扩展点则动态创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
}
createAdaptiveExtensionClass方法动态生成自适应扩展点代码
public class ExtensionLoader<T> {
private Class> createAdaptiveExtensionClass() {
// 动态生成自适应扩展点代码
// 此方法会校验是否满足以下任意一个条件
// 1.方法必须包含URL输入参数
// 2.方法必须包含get方法且返回值为URL
String code = createAdaptiveExtensionClassCode();
// javassist编译生成class对象
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
}
StockSpiService$Adaptive为动态生成自适应扩展点代码,我们可以看到URL这个参数作用,可以类比URL为路由器,自适应扩展点根据输入参数决定路由到哪个服务,如果没有值则默认路由到stockA服务
package com.java.front.dubbo.spi.api.stock;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class StockSpiService$Adaptive implements com.java.front.dubbo.spi.api.stock.StockSpiService {
public boolean reduceStock1(java.lang.String arg0, org.apache.dubbo.common.URL arg1) {
if (arg1 == null)
throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = url.getParameter("bizType", "stockA");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.java.front.dubbo.spi.api.stock.StockSpiService) name from url(" + url.toString() + ") use keys([bizType])");
com.java.front.dubbo.spi.api.stock.StockSpiService extension = (com.java.front.dubbo.spi.api.stock.StockSpiService) ExtensionLoader.getExtensionLoader(com.java.front.dubbo.spi.api.stock.StockSpiService.class).getExtension(extName);
return extension.reduceStock1(arg0, arg1);
}
public boolean reduceStock2(com.java.front.dubbo.spi.model.StockReduceModel arg0) {
if (arg0 == null)
throw new IllegalArgumentException("com.java.front.dubbo.spi.model.StockReduceModel argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.java.front.dubbo.spi.model.StockReduceModel argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("bizType", "stockA");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.java.front.dubbo.spi.api.stock.StockSpiService) name from url(" + url.toString() + ") use keys([bizType])");
com.java.front.dubbo.spi.api.stock.StockSpiService extension = (com.java.front.dubbo.spi.api.stock.StockSpiService) ExtensionLoader.getExtensionLoader(com.java.front.dubbo.spi.api.stock.StockSpiService.class).getExtension(extName);
return extension.reduceStock2(arg0);
}
}
6 自实现IOC
6.1 代码实例
第一步新增商品接口
package com.java.front.dubbo.spi.api.goods;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface GoodsSpiService {
public boolean buyGoods(String skuId, URL url);
}
第二步实现商品接口
package com.java.front.dubbo.spi.impl.goods;
import org.apache.dubbo.common.URL;
import com.java.front.dubbo.spi.api.goods.GoodsSpiService;
import com.java.front.dubbo.spi.api.stock.StockSpiService;
public class GoodsSpiAServiceImpl implements GoodsSpiService {
private StockSpiService stockSpiService;
public void setStockSpiService(StockSpiService stockSpiService) {
this.stockSpiService = stockSpiService;
}
@Override
public boolean buyGoods(String skuId, URL url) {
System.out.println("GoodsSpiAService buyGoods skuId=" + skuId);
stockSpiService.reduceStock1(skuId, url);
return false;
}
}
第三步新增配置文件
├─src
│ ├─main
│ │ └─resources
│ │ └─META-INF
│ │ ├─services
│ │ │ com.java.front.dubbo.spi.api.stock.GoodsSpiService
第四步新增配置文件内容
goodsA=com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
第五步运行测试代码
public class GoodsServiceTest {
public static void main(String[] args) {
test1();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(GoodsSpiService.class);
GoodsSpiService goodsService = extensionLoader.getExtension("goodsA");
Map map = new HashMap<>();
map.put("bizType", "stockA");
URL url = new URL(StringUtils.EMPTY, StringUtils.EMPTY, 1, map);
goodsService.buyGoods("skuId_111", url);
}
}
第六步输出测试结果
GoodsSpiAService buyGoods skuId=skuId_111
StockSpiAService reduceStock1 skuId=skuId_111
6.2 源码分析
源码流程如下图
injectExtension方法自实现IOC
public class ExtensionLoader<T> {
private T createExtension(String name) {
Class> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// instance = com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 自实现IOC
injectExtension(instance);
// 省略代码
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t);
}
}
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
// 遍历instance所有setxxx方法
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// method = public void com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl.setStockSpiService(com.java.front.dubbo.spi.api.stock.StockSpiService)
// pt = com.java.front.dubbo.spi.api.stock.StockSpiService
Class> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// property = stockSpiService
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : StringUtils.EMPTY;
// objectFactory = AdaptiveExtensionFactory
// AdaptiveExtensionFactory.getExtension依次执行SpiExtensionFactory、SpringExtensionFactory直到获取到StockSpiService对象赋值给object
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// instance = com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
// method = GoodsSpiAServiceImpl.setStockSpiService
// object = StockSpiService$Adaptive
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
}
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public T getExtension(Class type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
7 自实现AOP
7.1 装饰器模式
装饰器模式可以动态将责任附加到对象上,在不改变原始类接口情况下对原始类功能进行增强,并且支持多个装饰器的嵌套使用,实现装饰器模式需要以下组件:
(1) Component
抽象构件:核心业务抽象,使用接口或者抽象类
public abstract class Component {
public abstract void playFootBall();
}
(2) ConcreteComponent
具体构件:核心业务代码
public class ConcreteComponent extends Component {
@Override
public void playFootBall() {
System.out.println("踢足球");
}
}
(3) Decorator
抽象装饰器:继承Component抽象类并通过构造函数组合Component
public abstract class Decorator extends Component {
private Component component = null;
public Decorator(Component component) {
this.component = component;
}
@Override
public void playFootBall() {
this.component.playFootBall();
}
}
(4) ConcreteDecorator
具体装饰器:具体装饰代码
// 球袜装饰器
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
private void decorateMethod() {
System.out.println("换球袜");
}
@Override
public void playFootBall() {
this.decorateMethod();
super.playFootBall();
}
}
// 球鞋装饰器
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
private void decorateMethod() {
System.out.println("换球鞋");
}
@Override
public void playFootBall() {
this.decorateMethod();
super.playFootBall();
}
}
(5) 测试代码
public class TestDecoratorDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
component.playFootBall();
}
}
(6) 输出结果
换球鞋
换球袜
踢足球
7.2 代码实例
本章节在第二章节订单服务基础上进行扩展,第一步新增两个切面,一个日志切面,一个事务切面。我们可以理解切面为装饰器:切面实现了订单服务,并通过构造函数组合了订单服务
package com.java.front.dubbo.spi.impl.order;
// 日志切面
public class OrderSpiLogWrapper implements OrderSpiService {
private OrderSpiService orderSpiService;
public OrderSpiLogWrapper(OrderSpiService orderSpiService) {
this.orderSpiService = orderSpiService;
}
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiLogWrapper log start");
boolean result = orderSpiService.createOrder(order);
System.out.println("OrderSpiLogWrapper log end");
return result;
}
}
// 事务切面
public class OrderSpiTransactionWrapper implements OrderSpiService {
private OrderSpiService orderSpiService;
public OrderSpiTransactionWrapper(OrderSpiService orderSpiService) {
this.orderSpiService = orderSpiService;
}
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiTransactionWrapper begin");
boolean result = orderSpiService.createOrder(order);
System.out.println("OrderSpiTransactionWrapper commit");
return result;
}
}
第二步新增配置文件内容
orderSpiLogWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper
orderSpiTransactionWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper
第三步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test1();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getExtension("orderSpiAService");
orderSpiService.createOrder(new OrderModel());
}
}
第四步输出测试结果
OrderSpiLogWrapper log start
OrderSpiTransactionWrapper begin
OrderSpiAService createOrder
OrderSpiTransactionWrapper commit
OrderSpiLogWrapper log end
7.3 源码分析
源码流程如下图
createExtension方法可以看到切面通过构造函数装饰订单服务
public class ExtensionLoader<T> {
private T createExtension(String name) {
Class> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// instance = com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 自实现IOC
injectExtension(instance);
// 所有切面
// wrapperClasses = [com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper,com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper]
Set> wrapperClasses = cachedWrapperClasses;
// 自实现AOP
// instance=(OrderSpiLogWrapper(OrderSpiTransactionWrapper(orderSpiAService)))
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class> wrapperClass : wrapperClasses) {
// 1.通过切面构造函数进行装饰
// 2.切面可能需要通过set注入属性所以执行injectExtension
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t);
}
}
}
loadClass方法设置cachedWrapperClasses
public class ExtensionLoader<T> {
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class> clazz, String name) throws NoSuchMethodException {
// clazz是否实现type
// type = com.java.front.dubbo.spi.api.order.OrderSpiService
// clazz = com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper
// clazz = com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper
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.");
}
// 省略代码
// clazz是不是wrapper
else if (isWrapperClass(clazz)) {
Set> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet>();
wrappers = cachedWrapperClasses;
}
// 新增至集合cachedWrapperClasses
wrappers.add(clazz);
}
// 省略代码
}
// clazz是否通过构造函数组合type
private boolean isWrapperClass(Class> clazz) {
try {
clazz.getConstructor(type);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
}
8 文章总结
本文通过代码实例与源码分析两种方式,详细分析了DUBBO SPI六个特性:指定具体扩展点、指定默认扩展点、类级别自适应扩展点、方法级自适应扩展点、自实现IOC、自实现AOP,希望本文对大家有所帮助。
JAVA前线
欢迎大家关注公众号「JAVA前线」查看更多精彩分享,主要内容包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时也非常欢迎大家加我微信「java_front」一起交流学习