Dubbo SPI-Wrapper
前言
在Dubbo SPI中是通过Wrapper实现AOP,对于AOP相信大家都不陌生,这里不做的过多的介绍,我们主要来了解Dubbo SPI中是如何使用Wrapper类以及实现的细节。
使用场景
Dubbo 中的一个扩展接口可以有多个扩展实现类,这些扩展实现类可能会包含一些相同的逻辑,如果在每个实现类中都写一遍,那么这些重复代码就会变得很难维护。因此Dubbo提供的自动包装特性(Wrapper),来解决这个问题。 Dubbo将多个扩展实现类的公共逻辑,抽象到Wrapper类中,同时Wrapper也实现了扩展接口,在获取真正的扩展实现对象时,相当于在其外面包装一层Wrapper对象,可以理解成一层装饰器,通过这样就可以在方法执行前后调用公共方法,也是一种AOP的体现。
举个栗子
定义接口;
@SPI
public interface WrapperDemo {
void test();
}
定义接口实现;
public class WrapperDemoImpl implements WrapperDemo{
@Override
public void test() {
System.out.println("WrapperDemoImpl test 方法执行");
}
}
创建计算耗时包装类以及Order包装类;
public class CalWasteTimeWrapper implements WrapperDemo {
private WrapperDemo wrapperDemo;
public CalWasteTimeWrapper(WrapperDemo wrapperDemo) {
this.wrapperDemo = wrapperDemo;
}
@Override
public void test() {
System.out.println("执行cal wrapper");
System.out.println("执行统计耗时开始");
long startTime = System.currentTimeMillis();
wrapperDemo.test();
System.out.println("执行耗时" + (System.currentTimeMillis() - startTime));
}
}
public class OrderWrapper implements WrapperDemo {
private WrapperDemo wrapperDemo;
public OrderWrapper(WrapperDemo wrapperDemo) {
this.wrapperDemo = wrapperDemo;
}
@Override
public void test() {
System.out.println("执行order wrapper");
wrapperDemo.test();
}
}
定义配置文件;
wrapperDemo=org.dubbo.spi.example.wrapper.WrapperDemoImpl
calWasteTimeWrapper=org.dubbo.spi.example.wrapper.CalWasteTimeWrapper
orderWrapper=org.dubbo.spi.example.wrapper.OrderWrapper
测试,这里我们会发现wrapper的顺序,越往下的越先被调用;
public class Test {
public static void main(String[] args) {
WrapperDemo wrapperDemo = ExtensionLoader
.getExtensionLoader(WrapperDemo.class)
.getExtension("wrapperDemo");
wrapperDemo.test();
}
}

源码
关于源码部分我们开始介绍时候已经带出来过关于包装类相关的介绍,这里我们主要挑重点代码进行介绍,核心可以分为两步,一步是加载时候,另外执行代码创建时候;
加载
加载就是在loadClass方法时候扩展类加载,判断是否是包装类进行加载,将所有的包装类的信息加载到ConcurrentHashSet中,对于包装类的判断也很简单,就是判断该类里面是否有type类型的构造参数,如果有就是包装类,否则会抛出异常, 返回false;
//包装类判断
else if (isWrapperClass(clazz)) {
//缓存包装类
cacheWrapperClass(clazz);
}
//使用ConcurrentHashSet缓存
private void cacheWrapperClass(Class<?> clazz) {
if (cachedWrapperClasses == null) {
cachedWrapperClasses = new ConcurrentHashSet<>();
}
cachedWrapperClasses.add(clazz);
}
//判断是否是包装类
private boolean isWrapperClass(Class<?> clazz) {
try {
clazz.getConstructor(type);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
创建
创建部分是在createExtension方法中,对包装类进行创建,对于instance最后生成是WrapperDemo类型对象,里面包含OrderWrapper和CalWasteTimeWrapper对象,所以在最后调用的时候都进行输出,于是乎就有了Aop的效果;

@SuppressWarnings("unchecked")
private T createExtension(String name, boolean wrap) {
//从配置文件中加载所有扩展类,形成配置项名称与配置类的映射关系
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//采用反射创建对应实例
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//向对象中注入依赖的属性 IOC自动装配
injectExtension(instance);
if (wrap) {
//创建Wrapper扩展对象
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
//将当前instance作为参数创建wrapper实例,然后向wrapper实例中注入属性值
//并将wrapper实例赋值给instance
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
//环境初始化
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
结束
欢迎大家点点关注,点点赞!

Dubbo SPI-Wrapper的更多相关文章
- dubbo SPI设计
SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以在运行时, ...
- 【Dubbo源码阅读系列】之 Dubbo SPI 机制
最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解.如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出. Dubbo SPI 介绍 Java ...
- 搞懂Dubbo SPI可拓展机制
前言 阅读本文需要具备java spi的基础,本文不讲java spi,please google it. 一.Dubbo SPI 简介 SPI(Service Provider Interface) ...
- Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI
SPI 全称为 Service Provider Interface,是一种服务发现机制.当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类.所以在程序中并没有直接指定使用接口的哪个实 ...
- Dubbo——SPI及自适应扩展原理
文章目录 引言 正文 一.什么是SPI? 1. Java SPI的实现 2. Dubbo SPI实现原理 由配置文件得到的猜想 SPI源码 二.自适应扩展机制 三.Dubbo IOC 总结 引言 Du ...
- Dubbo SPI源码解析①
目录 0.Java SPI示例 1.Dubbo SPI示例 2.Dubbo SPI源码分析 SPI英文全称为Service Provider Interface.它的作用就是将接口实现类的全限定名 ...
- Dubbo SPI 机制源码分析(基于2.7.7)
Dubbo SPI 机制涉及到 @SPI.@Adaptive.@Activate 三个注解,ExtensionLoader 作为 Dubbo SPI 机制的核心负责加载和管理扩展点及其实现.本文以 E ...
- Dubbo2.7的Dubbo SPI实现原理细节
总结/朱季谦 本文主要记录我对Dubbo SPI实现原理的理解,至于什么是SPI,我这里就不像其他博文一样详细地从概念再到Java SPI细细分析了,直接开门见山来分享我对Dubbo SPI的见解. ...
- 理解 Dubbo SPI 扩展机制
写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...
- dubbo源码分析--dubbo spi解析
1. 什么叫SPI? 简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射. 2. java SPI与Dubbo SPI有什么区别 (此图来自网上,我没有刻意去截图) 然后在这个文件 ...
随机推荐
- 记录--Openlayers 高德腾讯、百度、天地图坐标相互转换
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 在地图开发过程中,坐标的转换是很常用的功能,国内的话一般西安80(EPSG:4610).北京54(EPSG:2433)转WGS84比较多, ...
- 开发必会系列:为什么要用spring
Spring是于2003 年兴起的一个轻量级的Java 开发框架,开源的,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中 ...
- [开源初探]screenshot-to-code:将截图转化为代码
介绍说明 将截图转化为 HTML 代码的工具.该项目可以将屏幕截图转化为 HTML/JS/Tailwind CSS 代码,它使用 GPT-4 Vision 生成代码.DALL-E 3 生成相似的图片. ...
- 【K8S】如何进入kubernetes的一个pod
如何进入kubernetes的一个pod呢,其实和进入docker的一个容器相似: 进入docker容器 : docker exec -ti <your-container-name> / ...
- 感悟:FPGA的并行处理与PC的多线程处理
前言 FPGA的并行设计是其高速处理的核心之一, 通过并行地处理大量的数据实现预期的功能; PC的多线程设计则是处理大量的内容而衍生出的一种处理方式, 其本质是利用CPU的高速处理能力, 将单个线程以 ...
- 未来已来,OpenHarmony 3.2 Release发布,迈入发展新阶段
2023年4月9日,在社区开发者的期盼中,在春风送暖万物更新的季节里,我们迎来了OpenAtom OpenHarmony(以下简称"OpenHarmony")3.2 Relea ...
- 如何在 Python 中执行 MySQL 结果限制和分页查询
Python MySQL 限制结果 限制结果数量 示例 1: 获取您自己的 Python 服务器 选择 "customers" 表中的前 5 条记录: import mysql.c ...
- 记录C++,base64解码写PDF文件遇到的坑
不得不bb一下, 场景:用户传base64数据,我生成PDF文件保存到指定路径下 背景:在前人写好的工程上增加这个功能,工程中有base64的.h, .cpp 文件,我试了base64编码没有问题,所 ...
- vc修改ocx控件classID
背景: 客户希望,classID 为客户自已定义好的一个 ID ,由于我们自动生成的 classID 和客户定义的 ID 不同,所以我们需要修改 classID 为客户指定的 ID 解决方法: 1.打 ...
- 给你安利一款鲁班大叔开发的maven依赖搜索神器
写在前面 一款好的插件往往能提高我们的开发效率.今天就给大家安利一款maven 依赖搜索插件. 插件是自己一直关注的鲁班大叔开发的,用了几天真的好用 废话不多说,我们就来看看这是一款什么插件 一.ma ...