Java-SPI机制

Java--SPI机制

SPI全称为Service Provider Interface,是JDK内置的一种服务提供发现机制。简单来说,它就是一种动态替换发现机制。例如:有个接口想在运行时才发现具体的实现类,那么你只需要在程序运行前添加一个实现即可,并把新加的实现描述给JDK即可。此外,在程序的运行过程中,也可以随时对该描述进行修改,完成具体实现的替换。

Java提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的SPI有JDBC、JCE、JNDI、JAXP和JBI等。

这些SPI的接口是由Java核心库来提供,而SPI的实现则是作为Java应用所依赖的jar包被包含进类路径(CLASSPATH)中。例如:JDBC的实现mysql就是通过maven被依赖进来。

那么问题来了,SPI的接口是Java核心库的一部分,是由引导类加载器(Bootstrap Classloader)来加载的。SPI的实现类是由系统类加载器(System ClassLoader)来加载的。

引导类加载器在加载时是无法找到SPI的实现类的,因为双亲委派模型中规定,引导类加载器BootstrapClassloader无法委派系统类加载器AppClassLoader来加载。这时候,该如何解决此问题?

线程上下文类加载由此诞生,它的出现也破坏了类加载器的双亲委派模型,使得程序可以进行逆向类加载。

下面,我们就用具体的代码来说明逆向类加载。不过,在距离之前,还是想对spi的使用进行一个简单的说明。

spi使用

首先,通过一张图来看,完成spi的实现,需要哪些操作,需要遵循哪些规范?

spi规范

1.代码编写

既然是spi,那么就必须先定义好接口。其次,就是定义好接口的实现类。

2.创建一个文件夹

在项目的\src\main\resources\下创建\META-INF \services目录(笔者在网上找了很多文章,很多都没有告知具体这个文件夹放在哪,放在其他位置下无法加载得到)

3.文件夹下增加配置文件

在上面META-INF \services的目录下再增加一个配置文件,这个文件必须以接口的全限定类名保持一致,例如:com.jiaboyan.test.HelloService

4.配置文件增加描述

上面介绍spi时说道,除了代码上的接口实现之外,你还需要把该实现的描述提供给JDK。那么,此步骤就是在配置文件中撰写接口实现描述。很简单,就是在配置文件中写入具体实现类的全限定类名,如有多个便换行写入。

5.使用JDK来载入

编写main()方法,输出测试接口。使用JDK提供的ServiceLoader.load()来加载配置文件中的描述信息,完成类加载操作。

代码结构

接口定义:

public interface HelloService {

    void hello();
}

接口实现:

public class HelloService1Impl implements HelloService {

    @Override
    public void hello() {
        System.out.println("hello jiaboyan");
    }
}

public class HelloService2Impl implements HelloService {
    @Override
    public void hello() {
        System.out.println("hello world");
    }
}

添加JDK描述,在META-INF\services目录下:

com.jiaboyan.test.impl.HelloService1Impl
com.jiaboyan.test.impl.HelloService2Impl

编写main()方法:

public class Test {

    public static void main(String[] agrs) {
        ServiceLoader<HelloService> loaders = ServiceLoader.load(HelloService.class);
        for (HelloService helloService : loaders) {
            helloService.hello();
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏aoho求索

认证鉴权与API权限控制在微服务架构中的设计与实现(三)

引言: 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第三篇,本文重点讲解token以及API级别的鉴权。本文对涉及到的大部分代码进行了分析...

5284
来自专栏Seebug漏洞平台

Spring Security OAuth RCE (CVE-2016-4977) 漏洞分析

Author: p0wd3r (知道创宇404安全实验室) Date: 2016-10-17 0x00 漏洞概述 1.漏洞简介 Spring Security...

3798
来自专栏walterlv - 吕毅的博客

(2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序

发布于 2018-07-25 13:20 更新于 2018-07...

632
来自专栏happyJared

IDEA快捷键拆解系列(十八):Live Templates篇

  首先,我们要知道Live Template是在哪里定义的,先按Ctrl + Shift + S进入设置,接着在输入框输入“Live Template”就可以...

583
来自专栏学习力

《Java从入门到放弃》框架入门篇:springMVC数据校验

1524
来自专栏乐沙弥的世界

[INS-30507] Empty ASM disk group

    最近的Oracle 11g RAC安装碰到了INS-30507错误,也就是在grid安装到创建ASM磁盘组的时候找不到任何候选磁盘,google了N多安...

763
来自专栏跟着阿笨一起玩NET

一个日志类 LogUtil

691
来自专栏JMCui

SpringMVC处理multipart请求.

一、简述     multipart格式的数据会将一个表单拆分为多个部分(part),每个部分对应一个输入域。在一般的表单输入域中,它所对应的部分中会放置文本型...

3135
来自专栏ytkah

用laravel dingo/api创建简单的api

如果你曾经使用过 API 你就会知道大多数服务都来自子域或前缀。前缀或子域是必须的,但只需要一个。请避免使用版本号作为你的前缀或子域,因为版本控制是通过 hea...

975
来自专栏ImportSource

学会一个JVM插件:使用HSDIS反汇编JIT生成的代码

HSDIS是一个Java官方推荐 HotSpot虚拟机JIT编译代码的反汇编插件。我们有了这个插件后,通过JVM参数-XX:+PrintAssembly就可以加...

47811

扫码关注云+社区