学java的,想进阶spring和springboot源码必看的, 我自己都撸了好几遍了,面试和工作中常用的,经常需要自己扩展一些工具类
想要更多资料的call me,各种大厂视频,加薪80%,40万不难
PI
什么是SPI
我们在“调用方”和“实现方”之间需要引入“接口”,可以思考一下什么情况应该把接口放入调用方,什么时候可以把接口归为实现方。先来看看接口属于实现方的情况,这个很容易理解,实现方提供了接口和实现,我们可以引用接口来达到调用某实现类的功能,这就是我们经常说的api,它具有以下特征:
- 概念上更接近实现方
- 组织上位于实现方所在的包中
- 实现和接口在一个包中
当接口属于调用方时,我们就将其称为spi,全称为:service provider interface,spi的规则如下:
- 概念上更依赖调用方
- 组织上位于调用方所在的包中
- 实现位于独立的包中(也可认为在提供方中)
为什么要使用SPI
面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候不用在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移到了程序之外。
案例分析
java spi
在jdk6里面引进的一个新的特性ServiceLoader,ServiceLoader可以通过service provider的配置文件来装载指定的service provider。当服务的提供者,提供了服务接口的一种实现之后,我们只需要在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
java spi案例
1、定义接口,创建实现类
public interface HelloService {
void sayhello();
}
public class HelloService1Impl implements HelloService {
@Override
public void sayhello() {
System.out.println("hello one!");
}
}
public class HelloService2Impl implements HelloService {
@Override
public void sayhello() {
System.out.println("hello two!");
}
}
2、创建配置文件
在\src\main\resources\下创建\META-INF\services目录,在其下新增配置文件,这个文件必须以接口的全限定类名保持一致,如 com.example.extra.spi.service.HelloService
3、新增配置
在配置文件 com.example.extra.spi.service.HelloService内写入具体实现类的全限定类名
com.example.extra.spi.service.impl.HelloService1Impl
com.example.extra.spi.service.impl.HelloService2Impl
4、注入spring容器
为了结合spring,我们将spi的实例交给spring容器管理
@Component
public class SpiBeanRegister implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//通过ServiceLoader加载实例
ServiceLoader<HelloService> loaders = ServiceLoader.load(HelloService.class);
for (HelloService helloService : loaders) {
String className = helloService.getClass().getSimpleName();
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(helloService.getClass());
// 将Bean 的定义注册到Spring环境
beanDefinitionRegistry.registerBeanDefinition(className, rootBeanDefinition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
5、测试
这里我们将条件和实现类通过枚举做配置化
public enum HelloMethed {
METHED_A("A","HelloService1Impl"),
METHED_B("B","HelloService2Impl");
HelloMethed(String code, String methed) {
this.code = code;
this.methed = methed;
}
private String code;
private String methed;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMethed() {
return methed;
}
public void setMethed(String methed) {
this.methed = methed;
}
public static String getMethedName(String code){
for (HelloMethed methed : HelloMethed.values()){
if (methed.getCode().equals(code)){
return methed.getMethed();
}
}
return null;
}
}
测试类
@Configuration
public class HelloConfiguration {
@Autowired
private Map<String, HelloService> activityBaseMap;
@Bean
public Test test(){
String methedA = HelloMethed.getMethedName("A");
String methedB = HelloMethed.getMethedName("B");
HelloService helloService1 = (HelloService) activityBaseMap.get(methedA);
helloService1.sayhello();
HelloService helloService2 = (HelloService) activityBaseMap.get(methedB);
helloService2.sayhello();
return new Test();
}
}
效果展示
DriverManager spi案例
Dubbo的扩展机制
Spring自定义标签
Springboot自动装配
后话
Spring后置处理器
BeanFactoryPostProcessor
bean工厂的后置处理器,在bean定义(bean definitions)加载完成后,bean尚未初始化前执行。
BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor继承于BeanFactoryPostProcessor。其自定义的方法postProcessBeanDefinitionRegistry会在bean定义(bean definitions)将要加载,bean尚未初始化前真执行,即在BeanFactoryPostProcessor的postProcessBeanFactory方法前被调用。
BeanPostProcessor
bean的后置处理器,主要在bean初始化前后工作。
InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor继承于BeanPostProcessor,主要在实例化bean前后工作,AOP创建代理对象就是通过该接口实现。