SPI
SPI1,2是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性和灵活性的机制。引入服务提供者(也即SPI接口的实现者),通过本地的注册发现获取到具体的实现类,轻松可插拔。
e.g. , 使用mysql驱动应用了该机制:
我也亲自实操了一下:
如上图,在META_INF/services/com.secbro2.Subscribe文件里指定了两个实现类。
接口类Subscribe.java :
package com.secbro2;
public interface Subscribe {
void follow();
}
第一个实现类MySubscribe :
package com.secbro2;
public class MySubscribe implements Subscribe {
@Override
public void follow() {
System.out.println("关注了 MySubscribe");
}
}
第二个实现类OtherSubscribe :
package com.secbro2;
public class OtherSubscribe implements Subscribe {
public void follow() {
System.out.println("关注了OtherSubscribe!");
}
}
调用者:
package com.secbro2;
import java.util.ServiceLoader;
public class Call {
public static void main(String[] args) {
ServiceLoader<Subscribe> services = ServiceLoader.load(Subscribe.class);
for (Subscribe sub : services) {
sub.follow();
}
ServiceLoader<Parent> serviceLoader = ServiceLoader.load(Parent.class);
for (Parent cl : serviceLoader) {
cl.fun();
}
}
}
/** output:
关注了 MySubscribe
关注了OtherSubscribe!
Son1 fun
Son2 fun
**/
其中Parent与Son1,Son2相对于Subscribe.java等,只是把接口与实现类换成了类与其子类的关系,也是照样生效的。
springboot的自动配置依赖于类似SPI的机制
javaGuide—SpringBoot 自动装配原理详解—如何实现一个 Starter (尚未实操)和 springboot的自动配置原理及流程总结 的总结 :
通过@SpringBootConfiguration 引入了@EnableAuoConfiguration (负责启动自动配置功能)
@EnableAutoConfiguration 引入了@lmport
Spring容器启动时: 加载loc容器时会解析@Import注解
@lmport导入了一个DeferredlmportSelector(它会SpringBoot的自动配置类的顺序在最后,这样方便我们扩展和覆盖)
然后读取所有的/META-INF/spring.factories文件(SPI)
过滤出所有AutoConfigurtionClass类型的类
最后通过@ConditioonXXX排除无效的自动配置类
Dubbo的SPI机制
JavaGuide—Dubbo 常见面试题总结—Dubbo 的 SPI 机制了解么?
SPI、双亲委派机制、线程上下文类加载器
为什么有的人称SPI破坏了类加载器的双亲委派机制?
类加载器的双亲委派机制决定了可见性(visibility)。可见性原则允许子类加载器查看父类加载器加载的所有类,但父类加载器看不到子类加载器的类。3
java.sql.DriverManager通过扫包的方式拿到指定的实现类,完成 DriverManager的初始化。
但是,java.sql.DriverManager是启动类加载器加载的,根据可见性原则,启动类加载器加载的DriverManager不可能拿到应用类加载器加载的实现类 。3
为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器 (Thread Context ClassLoader)3
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD) 。。。我们会发现不是用的本类的加载器去加载的,而用线程上下文的加载器去加载的。。。会将AppClassloader放到上下文线程中,上面拿到的类加载器也就是这里的AppClassloader了。4