一、SPI机制
SPI:Service Provider Interface,是一种服务发现机制
它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类,模块之间基于接口编程,实现可拔插
二、栗子
1.定义接口
package com.zz.spi;
public interface SPIService {
void execute();
}
2.实现类
SPIImpl1.java
package com.zz.spi;
public class SPIImpl1 implements SPIService {
@Override
public void execute() {
System.out.println("SPIImpl1 execute()");
}
}
SPIImpl2.java
package com.zz.spi;
public class SPIImpl2 implements SPIService {
@Override
public void execute() {
System.out.println("SPIImpl2 execute()");
}
}
3.定义文件
在ClassPath下,META-INF/services目录下创建一个以"接口全限定名"为命名的文件:
内容为实现类的全限定名:
com.zz.spi.SPIImpl1
com.zz.spi.SPIImpl2
4.使用
有两个类可以得到实现类的实例,sun.misc.Service和java.util.ServiceLoader
package com.zz.spi;
import sun.misc.Service;
import java.util.Iterator;
import java.util.ServiceLoader;
public class Main {
public static void main(String[] args) {
Iterator<SPIService> providers = Service.providers(SPIService.class);
ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
while (providers.hasNext()) {
SPIService service = providers.next();
service.execute();
}
System.out.println("- - - - - - - - - - - - - -");
Iterator<SPIService> iterator = load.iterator();
while (iterator.hasNext()) {
SPIService service = iterator.next();
service.execute();
}
}
}
结果:
三、JDBC中的应用
1.未用SPI机制
如果未用SPI机制,需要使用Class.forName()加载驱动
@Test
public void test1() throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
为什么需要加载驱动,查看源码,可以看出,当加载驱动时,会执行驱动类的static代码块,此代码块的作用是向DriverManager注册驱动,这样DriverManager.getConnection()便可以获得连接
2.使用SPI机制
对于高版本的jdk和mysql驱动,不再需要使用Class.forName()加载驱动
@Test
public void test2() throws SQLException {
String url = "jdbc:mysql://localhost:3306";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
分析:
对于com.mysql.cj.jdbc.Driver,其jar文件META-INF/services目录下创建了java.sql.Driver,内容是Driver实现类的全限定名
对于DriverManager类
driversIterator.next()会创建Driver实例,所以此时会执行com.mysql.cj.jdbc.Driver的代码,注册到DriverManager
四、其他
1.AutoService
可以使用google的AutoService组件会自动在编译后的ClassPath下生成文件META-INF/services/com.zz.spi.SPIService文件
添加maven依赖
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc2</version>
</dependency>
在实现类上加上类注解@AutoService
SPIImpl1.java
package com.zz.spi;
import com.google.auto.service.AutoService;
@AutoService(SPIService.class)
public class SPIImpl1 implements SPIService {
@Override
public void execute() {
System.out.println("SPIImpl1 execute()");
}
}
SPIImpl2.java
package com.zz.spi;
import com.google.auto.service.AutoService;
@AutoService(SPIService.class)
public class SPIImpl2 implements SPIService {
@Override
public void execute() {
System.out.println("SPIImpl2 execute()");
}
}