快速搞懂 SPI

目录

一、什么是 SPI

二、SPI 的使用场景

数据库驱动

日志框架

SPI 框架

三、SPI 实现

JDK SPI 实现


一、什么是 SPI

SPI( Service Provider Interface ),是 JDK 内置的一种服务提供发现机制( JDK 本身就带着的一个功能)。SPI 允许服务提供者在运行时动态地将实现类注入到系统中,从而实现组件的可插拔性和扩展性

通俗来说,就是我们定义了一套标准,别人去实现,然后在程序执行的时候,根据这个约定我们来发现其实现的具体的内容,来进行执行。

举个栗子:

要实现数据库的增删改查操作,但数据库由不同的公司或厂商去做,不同的公司、不同厂商 实现 CRUD 的方式是不同的。这样假如有一天要切换数据库,发现来自不同公司或厂商的数据库有不同的写法,对开发者来说是很麻烦的,不具备通用性了,因此就有了一种机制 ,提供一套接口,开发者直接使用接口里的方法实现对应功能,让不同厂商去实现这个接口,从而不再被动的去兼容数据库厂商,而是提供了一个规范 / 插件 / 发现机制 / 接口,让底层 / 下游的数据库厂商对接既定的接口,从而提供具体的实现,这就是 SPI 的一个作用。

--- 我是分割线---

【补充】所以 SPI 和动态代理完全不一样的,动态代理和反射比较类似,动态代理是反射的一种实现,动态代理只是“玩”自己里面的,“格局小”,而 SPI “玩”的是一种插件、是不同系统之间的对接,“格局更大”。

对于 SPI 机制而言,通常有三个角色

  • 服务接口( Service Interface ):定义一组接口或抽象类,表示一种服务或功能
  • 服务提供者接口( Service Provider Interface ):是服务接口的具体实现,提供了服务的具体功能(相当于上述栗子的厂商)
  • 服务加载器( Service Loader ):程序执行时,负责加载并实例化服务提供者,并将其注册到系统中,以供服务接口使用(有接口、有实现了,中间得有一个“运输者”,即服务加载器)

二、SPI 的使用场景

数据库驱动

不同的数据库厂商提供了自己的数据库驱动实现,这些实现都实现了同一个 JDBC 接口。JVM 在运行时可以动态加载适合的数据库驱动,使得开发者在不修改代码的情况下切换不同的数据库。

日志框架

日志框架也实现了该机制,例如 SLF4J (没有具体实现,就是一个接口)。开发者可以选择不同的日志实现(如 Logback、Log4j 等),并将其注入到日志框架中,从而灵活地切换日志实现。 

SPI 框架

其他很多框架也使用了该技术,例如 Dubbo(高性能 RPC 框架,Remote Procedure Call 远程过程调用协议)中,它们允许开发者通过 SPI 方式定义和管理应用程序的组件和扩展。

三、SPI 实现

JDK SPI 实现

首先,我们定义一个服务接口 MessageService ,表示一种消息服务:

public interface MessageService {
    void sendMessage(String message);
}

然后,我们定义两个实现类,分别表示不同的消息服务提供者:

public class EmailMessageService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}
public class SmsMessageService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

【注意】以上提供的这两个具体的实现,是 jar 包,在类里面是不会直接去使用 jar 包的,我们使用的是上层的类,所以此时我们需要依靠 SPI 将实现与接口“拼”到一块,怎么拼?在 Java 里是固定的写法,往下看哈~

接下来,在 src/main/resources/META-INF/services 目录下,创建一个名为 spi.MessageService (包名 + 类名)的文件(注意:文件的全路径名与服务接口的全限定名一致),内容如下:

spi.EmailMessageService
spi.SmsMessageService

这个文件里列出了所有实现了 MessageService 接口的类的全限定名,每行一个。

最后,我们编写一个测试类,使用 ServiceLoader 来加载并实例化实现类,并调用其方法:

import java.util.ServiceLoader;

public class SPIDemo {
    public static void main(String[] args) {
        ServiceLoader<MessageService> loader = ServiceLoader.load(MessageService.class);
        for (MessageService service : loader) {
            service.sendMessage("Hello, SPI!");
        }
    }

【注意】在这部分代码里,完全没有使用子类,使用的始终都是最上层的类,最上层的类是定义接口的,是不关心底层类的具体实现的,因为不同厂商是有不同实现的,所以在调用和使用的时候用的都是上层类,那此时在调用方法的时候能执行的原因就是通过加载器找到了服务提供者和服务接口,将这两联系起来,然后进行调用即可。


  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在LVGL中,可以使用`lv_disp_drv_t`结构体中的`rounder_cb`回调函数来实现快速填充。具体实现步骤如下: 1. 确认你的LVGL驱动支持`rounder_cb`回调函数。如果驱动支持,可以在`lv_disp_drv_t`结构体中设置回调函数指针。 ```c lv_disp_drv_t disp_drv; disp_drv.rounder_cb = your_rounder_function; ``` 2. 编写`rounder_cb`回调函数。这个函数的作用是将要绘制的区域大小进行调整,这样可以提高SPI传输的效率。 ```c static void your_rounder_function(lv_disp_drv_t *disp_drv, lv_area_t *area) { // 计算实际需要绘制的区域大小 int32_t w = lv_area_get_width(area); int32_t h = lv_area_get_height(area); int32_t x = area->x1; int32_t y = area->y1; // 将x,y调整到最接近的4的倍数 x = (x + 3) & ~0x3; // 将w调整到最接近的4的倍数 w = (w + 3) & ~0x3; // 更新area的值 lv_area_set(area, x, y, x + w - 1, y + h - 1); } ``` 3. 在绘制之前,调用`lv_disp_flush_ready()`函数,将绘制区域的信息传递给驱动程序。 ```c // 在绘制之前,调用your_rounder_function函数对绘制区域进行调整 lv_disp_t * disp; lv_disp_drv_t disp_drv; lv_area_t area; // ... // 调用your_rounder_function函数 disp_drv.rounder_cb(&disp_drv, &area); // 在绘制之前,调用lv_disp_flush_ready()函数,将绘制区域的信息传递给驱动程序 lv_disp_flush_ready(&disp_drv); ``` 4. 对SPI进行多字节快速填充。 ```c // SPI多字节快速填充 lv_disp_drv_t disp_drv; // ... lv_disp_flush_ready(&disp_drv); uint32_t color = your_color_value; // 计算填充区域大小 uint32_t size = lv_area_get_width(&area) * lv_area_get_height(&area); // 向SPI发送多字节数据 for (uint32_t i = 0; i < size; i++) { your_spi_send_function(color); } ``` 注意:`your_spi_send_function()`函数的实现需要根据你的硬件平台进行相应的调整。 ### 回答2: LVGL是一个开源的图形库,支持嵌入式设备上的图形显示。而SPI(Serial Peripheral Interface)是一种串行外设接口,常用于连接嵌入式设备和外部设备。 在LVGL中,提供了多字节快速填充功能,可以通过SPI接口实现高效的图形填充操作。通过SPI接口发送一连串的字节数据,可以快速将整个屏幕或者指定区域填充为指定的颜色。 使用SPI多字节快速填充的方法如下: 1. 配置SPI接口:首先,需要正确配置SPI接口,设置SPI的通信速率、数据位数等参数,以适配硬件设备。 2. 准备数据:在进行填充之前,需要准备好填充的数据。LVGL提供了丰富的绘图API,可以创建各种图形。将需要填充的图形数据存储在一个缓冲区中,以便发送给SPI接口。 3. 发送数据:将准备好的数据通过SPI接口发送给外部设备。使用SPI多字节传输的方式,可以一次性发送多个字节的数据,从而提高填充速度。 4. 刷新显示:完成数据发送后,需要及时刷新显示,以使填充的图形在屏幕上显示出来。LVGL提供了相应的刷新显示函数,可以根据需要选择调用。 SPI多字节快速填充可以极大地提高填充效率,特别适用于嵌入式设备上的实时图形显示。通过合理配置SPI接口和使用LVGL提供的绘图API,可以方便地实现多字节快速填充的功能,从而提升图形显示的性能和用户体验。 ### 回答3: LVGL是一个用于嵌入式GUI的开源图形库,支持各种显示驱动和触摸屏控制器。SPI是一种用于串行数据传输的通信协议,它可以在嵌入式系统中与外设进行通信。 在LVGL中,使用SPI进行多字节快速填充是一种使用SPI接口来快速将多个字节的数据传输到外设的方法,以实现图形元素的绘制。 具体实现方法是首先将要传输的字节数据存储在一个缓冲区中,然后通过SPI接口以一定的速率将这些数据发送到外设中。这种方法可以有效地减少数据传输的时间,提高绘制的速度和效率。 在使用SPI进行多字节快速填充时,需要注意以下几点: 1. 确保SPI接口的时钟速率足够高,以确保数据传输的效率。可以根据实际情况选择合适的时钟频率。 2. 要传输的数据需要预先存储在一个缓冲区中,可以使用LVGL提供的缓冲区管理功能来操作缓冲区。 3. 通过LVGL的接口函数来设置SPI的传输参数,包括时钟极性、相位和字节序等,以兼容外设的要求。 4. 使用LVGL提供的绘制接口函数来调用SPI多字节快速填充方法,传输数据到外设进行绘制操作。 综上所述,LVGL通过使用SPI进行多字节快速填充可以有效地提高绘制速度和效率,使嵌入式GUI的显示功能更加流畅和实时。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值