JAVA SPI+PostProcessor实现动态扩展

学java的,想进阶spring和springboot源码必看的, 我自己都撸了好几遍了,面试和工作中常用的,经常需要自己扩展一些工具类

想要更多资料的call me,各种大厂视频,加薪80%,40万不难

JAVA SPI+PostProcessor实现动态扩展

PI

什么是SPI

JAVA SPI+PostProcessor实现动态扩展

我们在“调用方”和“实现方”之间需要引入“接口”,可以思考一下什么情况应该把接口放入调用方,什么时候可以把接口归为实现方。先来看看接口属于实现方的情况,这个很容易理解,实现方提供了接口和实现,我们可以引用接口来达到调用某实现类的功能,这就是我们经常说的api,它具有以下特征:

  1. 概念上更接近实现方
  2. 组织上位于实现方所在的包中
  3. 实现和接口在一个包中

当接口属于调用方时,我们就将其称为spi,全称为:service provider interface,spi的规则如下:

  1. 概念上更依赖调用方
  2. 组织上位于调用方所在的包中
  3. 实现位于独立的包中(也可认为在提供方中)

为什么要使用SPI

面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候不用在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移到了程序之外。

案例分析

java spi

JAVA SPI+PostProcessor实现动态扩展

在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();

    }

}

效果展示

JAVA SPI+PostProcessor实现动态扩展

 DriverManager spi案例

Dubbo的扩展机制

Spring自定义标签

Springboot自动装配

后话

Spring后置处理器

BeanFactoryPostProcessor

bean工厂的后置处理器,在bean定义(bean definitions)加载完成后,bean尚未初始化前执行。

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor继承于BeanFactoryPostProcessor。其自定义的方法postProcessBeanDefinitionRegistry会在bean定义(bean definitions)将要加载,bean尚未初始化前真执行,即在BeanFactoryPostProcessor的postProcessBeanFactory方法前被调用。

JAVA SPI+PostProcessor实现动态扩展

 BeanPostProcessor

bean的后置处理器,主要在bean初始化前后工作。

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor继承于BeanPostProcessor,主要在实例化bean前后工作,AOP创建代理对象就是通过该接口实现。

JAVA SPI+PostProcessor实现动态扩展

JAVA SPI+PostProcessor实现动态扩展

上一篇:Linux SPI总线(2) - SPI通用接口层


下一篇:通信方案软件设计(环形动态申请内存,支持USART+IIC+SPI+CAN协议