您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页java-spi

java-spi

来源:华佗小知识

Java中的SPI(Service Provider Interface)是一种服务发现机制,允许服务提供者动态地为服务接口提供实现。SPI机制通过在运行时查找并加载服务实现类,从而实现了松耦合的设计。以下是SPI的工作原理和机制:

SPI的工作原理

SPI机制的使用

步骤一:定义服务接口

package com.example;

public interface MyService {
    void execute();
}

步骤二:实现服务接口

package com.example.impl;

import com.example.MyService;

public class MyServiceImpl implements MyService {
    @Override
    public void execute() {
        System.out.println("Executing MyServiceImpl");
    }
}

com.example.impl.MyServiceImpl

步骤四:使用ServiceLoader加载服务

package com.example;

import java.util.ServiceLoader;

public class Main {
    public static void main(String[] args) {
        ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
        for (MyService service : serviceLoader) {
            service.execute();
        }
    }
}

运行机制

  1. 加载配置文件:ServiceLoader会从类路径下的META-INF/services目录中查找名为com.example.MyService的文件。
  2. 解析配置文件:读取文件内容,获取实现类的全限定名。
  3. 加载实现类:使用反射机制实例化实现类。
  4. 提供服务:将实现类的实例返回给应用程序进行使用

SPI的优点

  1. 松耦合:实现类和接口解耦,方便替换实现。
  2. 扩展性好:新服务提供者只需要实现接口并在配置文件中声明即可,无需修改客户端代码。
  3. 灵活性高:可以在运行时动态加载和替换服务实现。
    SPI的注意事项
  4. 安全性:确保服务实现类的安全性,防止加载恶意实现。
  5. 性能:由于需要在运行时进行反射和解析,可能会有一定的性能开销。
  6. 类路径管理:确保配置文件和实现类正确放置在类路径下。

注:

  1. 放到META-INF/services下的原因:
  2. META-INF/services目录下创建的文件应以服务接口的全限定名命名,文件内容则是服务实现类的全限定名。具体来说:
  • 文件名:应该是服务接口的全限定名。
  • 文件内容:应该是服务提供者(实现类)的全限定名。
project-root/
 ├── src/
 │   ├── main/
 │   │   ├── java/
 │   │   │   ├── com/
 │   │   │   │   ├── example/
 │   │   │   │   │   ├── MyService.java
 │   │   │   │   │   └── impl/
 │   │   │   │   │       └── MyServiceImpl.java
 │   │   ├── resources/
 │   │   │   └── META-INF/
 │   │   │       └── services/
 │   │   │           └── com.example.MyService
 └── pom.xml (or build.gradle if using Gradle)

关键点

  • 配置文件的命名:必须与服务接口的全限定名完全一致。
  • 配置文件的内容:应该是服务实现类的全限定名,可以包含多个实现类,每行一个。
  • 类路径管理:确保 META-INF/services 目录及其文件在运行时可见(通常会在项目的 src/main/resources 目录下创建该结构)。
  • Java的SPI机制是半自动的,意思是:
    • 自动:配置文件和实现类被正确放置在类路径中,ServiceLoader会自动找到这些配置文件并加载相应的实现类。
    • 手动:你必须手动调用ServiceLoader来执行加载过程。

springboot中使用

project-root/
 ├── src/
 │   ├── main/
 │   │   ├── java/
 │   │   │   ├── com/
 │   │   │   │   ├── example/
 │   │   │   │   │   ├── MyService.java
 │   │   │   │   │   ├── MyServiceAutoConfiguration.java
 │   │   │   │   │   └── impl/
 │   │   │   │   │       └── MyServiceImpl.java
 │   │   ├── resources/
 │   │   │   └── META-INF/
 │   │   │       └── spring.factories
 └── pom.xml (or build.gradle if using Gradle)

前面一样,后面创建一个自动配置类,例如MyServiceAutoConfiguration:

package com.example;

import com.example.impl.MyServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyServiceAutoConfiguration {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

创建spring.factories文件
在src/main/resources/META-INF/spring.factories中创建文件,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyServiceAutoConfiguration

应用主类为:

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Main.class, args);
        MyService myService = context.getBean(MyService.class);
        myService.execute();
    }
}

解释

  • spring.factories文件:这是Spring Boot用来配置自动配置类的方式。你将自动配置类(如MyServiceAutoConfiguration)的全限定名放在EnableAutoConfiguration条目下。
  • 自动配置类:MyServiceAutoConfiguration类中定义了一个@Bean方法,用来创建并注册MyService实现(MyServiceImpl)到Spring应用上下文。
  • Spring Boot应用:当Spring Boot启动时,它会自动扫描spring.factories文件,并加载配置的自动配置类,从而自动配置MyService实现。

通过这种方式,Spring Boot可以在启动时自动加载和配置你的服务实现,使得你的服务可以在应用的任何地方通过依赖注入(如通过@Autowired或ApplicationContext#getBean)来使用。

使用 spring.factories 文件和 Spring Boot 的自动配置机制可以被看作是一种 SPI(Service Provider Interface)实现方式,不过它与传统的 Java SPI 有所不同。让我们详细看看两者的区别和实现方式:
传统 Java SPI

    • 定义服务接口。
    • 实现服务接口。
    • 在 META-INF/services 目录下创建文件,文件名为服务接口的全限定名,内容为实现类的全限定名。
    • 使用 ServiceLoader 动态加载服务提供者。
- 定义服务接口。
- 实现服务接口。
- 创建自动配置类,将服务实现注册为 Spring Bean。
- 在 META-INF/spring.factories 文件中配置自动配置类。
- Spring Boot 自动加载和配置服务提供者。

比较

  • 传统 Java SPI:
    • 配置文件位置:META-INF/services
    • 配置文件内容:服务实现类的全限定名
    • 动态加载机制:ServiceLoader
    • 适用范围:所有 Java 应用
  • Spring Boot 自动配置机制:
    • 配置文件位置:META-INF/spring.factories
    • 配置文件内容:自动配置类的全限定名
    • 动态加载机制:Spring Boot 自动配置
    • 适用范围:Spring Boot 应用

在Spring Boot中,spring.factories文件不仅限于org.springframework.boot.autoconfigure.EnableAutoConfiguration配置条目,实际上可以包含许多不同类型的配置。以下是一些常用的配置条目:

  1. EnableAutoConfiguration:自动配置类
  2. ApplicationListener:应用程序事件
  3. EnvironmentPostProcessor:环境后处理器

EnableAutoConfiguration
这是最常用的条目,用于指定自动配置类。这些类将在Spring Boot应用启动时自动加载并应用。
ApplicationListener
用于指定应用程序事件,这些在特定事件(如应用启动或上下文刷新)发生时被调用。
EnvironmentPostProcessor
用于在Spring环境初始化后执行一些定制化的处理。

使用implements CommandLineRunner

在Spring Boot中,虽然实现CommandLineRunner接口可以用于在应用启动后执行特定代码,但这与传统的SPI机制有所不同。CommandLineRunner是在Spring Boot应用启动完成并且所有的Spring Beans都已经初始化之后运行的一个接口。

如果你想在Spring Boot中实现类似SPI的启动方式,可以结合使用CommandLineRunner和Spring的自动配置机制(如spring.factories文件)来实现。在这种情况下,CommandLineRunner可以作为一种触发机制,用于在Spring Boot启动时执行一些自定义逻辑。

  1. 确认类路径:确保你的类路径正确,并且所有类文件和配置文件都在正确的位置。
  2. 配置类(WsDatasourceConfig):
  • 确保你的配置类上有正确的注解:@Configuration。
package cn.example.datasource.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class WsDatasourceConfig {

    @Bean
    public WsDatasourceBootstrap wsDatasourceBootstrap() {
        return new WsDatasourceBootstrap();
    }
}

启动类(WsDatasourceBootstrap):

  • 确保你的启动类实现了CommandLineRunner接口。
  • 确保ConfigurableEnvironment被正确注入。
@Component
public class WsDatasourceBootstrap implements CommandLineRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(WsDatasourceBootstrap.class);

    @Autowired
    private ConfigurableEnvironment environment;

    @Override
    public void run(String... args) throws Exception {
        YmlUtils.environment = environment;
        LOGGER.info("已就绪");
    }
}

spring.factories文件

  • 确保你的spring.factories文件路径为src/main/resources/META-INF/spring.factories。
  • 确保文件内容格式正确。

然后启动项目,日志会打印“已就绪”

ConfigurableEnvironment
ConfigurableEnvironment是Spring Framework中的一个接口,它扩展了Environment接口,提供了对环境属性进行配置的能力。在Spring应用中,ConfigurableEnvironment通常用于访问和操作环境属性(如系统属性、环境变量、应用程序属性等),并且可以通过PropertySource添加或修改环境属性。

ConfigurableEnvironment 的用途

  1. 环境变量和系统属性访问:
  • 可以访问系统属性、环境变量以及各种配置源(如application.properties或application.yml文件)的属性。
    添加或修改属性源:
  • 可以动态地添加、修改或删除属性源,从而改变应用程序的行为。
  1. 配置文件(Profiles)管理:
  • 设置激活的配置文件:可以在运行时激活或切换配置文件,以便在不同环境(如开发、测试、生产)之间切换。
  • 设置默认配置文件:可以设置应用程序的默认配置文件。
  1. 动态调整配置:
  • 在应用程序运行时,可以动态调整和更新配置属性,而无需重启应用。
  1. 管理属性源(Property Sources):
  • 添加属性源:可以在运行时添加自定义的属性源,例如从数据库或外部服务加载的配置。
  • 修改属性源顺序:可以动态调整属性源的优先级和顺序。
  • 删除属性源:可以在运行时删除不需要的属性源。

environment.getProperty(key);获取系统值…

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务