美文网首页
Spring-SPI

Spring-SPI

作者: 16325 | 来源:发表于2020-02-20 16:32 被阅读0次

    SPI

    设计模式的六大原则中的开闭原则(Open Close Principle),意思是 对扩展开放,对修改关闭。所谓的“对外扩展”就是框架余留一个接口(途径)给外部进行修改内部的状态,实现不改变原有代码就可以改变代码

    动态

    有了接口,怎么实例化得到一个具体的实现类呢? 工厂方法,配置文件,都可以。还有,就是这篇所讲的SPI机制。是 Java 原生实现用于被第三方实现或余留的扩展 API。


    image.png

    上图为 SPI 实现的思路。我们可以看到,实际上是“调用方”只需要对接“标准的服务接口”,而实际上屏蔽了“服务提供方”的 A 和 B,这就给我们在实现上面提供了很大的灵活性。

    实现一个小栗子

    // 实现一个接口,作为标准服务接口
    public interface StandardService {
        public void offerService();
    }
    
    public class AStandardService implements StandardService {
        public void offerService() {
            System.out.println("我是服务 A,我可以为你提供 A 服务");
        }
    }
    
    public class BStandardService implements StandardService {
        public void offerService() {
            System.out.println("我是服务 B,我可以为你提供 B 服务");
        }
    }
    

    这时候我们的“服务”已经写好了。但是怎么让别人发现呢?其实 Java SPI 的规范会告诉你,你只需要在 classpath 下的 META-INF/services 文件夹里,放一个以“标准服务接口”的全限定名为名的文件,然后文件内写“服务提供方”的全限定名即可。如有多个,分行写。

    根据 SPI 的约定,那我就直接在 META-INF/services/ 下创建一个名为 com.jc.demov20200215.service.StandardService,内容为 com.jc.demov20200215.service.impl.AStandardService

    接下来就是调用方见证奇迹的时候了

    @SpringBootApplication
    public class Demov20200215Application {
    
        public static void main(String[] args) {
            ServiceLoader<StandardService> serviceLoader = ServiceLoader.load(StandardService.class);
            serviceLoader.forEach(StandardService::offerService);
        }
    }
    

    Spring.factories In Spring Boot

    • Spring 中也有类似于 Java SPI 这样扩展的地方。所以我们直接写一个关于如何使用 Spring Boot 的 Springfactories 机制进行扩展。
    • 但是说明一点,Spring 余留这个扩展机制很大原因不是为了给我们去加载自定义 Bean,而是为了让我们去扩展 Spring 其内部的 Bean,自定义框架想要的效果。
    • 例如说,我们学 Spring Framework 的时候,我们都知道在 web.xml 里面配置一个 ApplicationContextInitializer 的类。这个类的作用是框架启动时进行初始化数据/配置等。那么问题来了,假设我们有一些初始化参数想让 Spring 帮我们初始化,那我们怎么办呢?答案是:实现 ApplicationContextInitializer 接口,然后里面加入我们所初始化的参数。但是问题又来了,那我们怎么把我们的实现类告诉 Spring 呢?难不成是@Autowired ?不,很显然不行的!
    • 其实这里的原理跟 Java SPI 思考原理一样的。Spring 余留了 Spring factories 这种机制,就是它只关心你的东西放在哪里,不关心你怎么实现!

    创建一个实现了 ApplicationContextInitializer 的类:

    public class FirstInitializer implements ApplicationContextInitializer {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
            Map<String, Object> map = new HashMap<>();
            map.put("key","value");
            MapPropertySource mapPropertySource = 
                    new MapPropertySource("firstInitializer", map);
            environment.getPropertySources().addLast(mapPropertySource);
            System.out.println("firstInitializer finish");
        }
    }
    

    我们写好了自定义的 Initializer,现在我们要告诉 Spring 它我们的 Initializer 放在哪里!还记得上面的 Java SPI 吗?它是放在了classpath 的 META-INF/services 文件夹下的。而 Spring 是放在 它是放在了classpath 下的 META-INF/spring.factories 文件(如果有多个 initializer 可以使用逗号分割)。

    org.springframework.context.ApplicationContextInitializer=com.jc.demov20200215.initializer.FirstInitializer

    public class TestService implements ApplicationContextAware {
        
        private ApplicationContext applicationContext;
    
        public ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.getApplicationContext().getEnvironment().getProperty("key");   
        }
    }
    

    相关文章

      网友评论

          本文标题:Spring-SPI

          本文链接:https://www.haomeiwen.com/subject/syixqhtx.html