浅析Java与Spring中的SPI机制并利用SpringBoot的自动装配机制实现自己的starter依赖

本文总阅读量
本文最后更新于2 分钟前,文中所描述的信息可能已发生改变。

本文分别介绍了 Java 中与 Spring 中提供的 SPI 机制,并且还介绍了如何使用 SpringBoot 的自动配置编写一个自己的 starter 依赖。

Java当中提供的SPI

在目录resource/META-INF/services下以接口全限定名作为文件名,文件内容为其实现类,一个文件对应一个接口。

文件名为Animals接口的全限定名com.ecjtu.api.Animals文件内容为其实现类的全限定类名,一行一个。

txt
com.ecjtu.api.Cat  
com.ecjtu.api.Dog

定义接口

java
public interface Animals {  
	void call();  
}

定义实现类

java
//Cat类
public class Cat implements Animals {  
	@Override  
	public void call() {  
		System.out.println("我是猫:斯!");  
	}  
}

//Dog类
public class Dog implements Animals {  
	@Override  
	public void call() {  
		System.out.println("我是狗:giao!");  
	}  
}

Java提供的SPI使用ServiceLoader来加载对应接口的实现类

java
ServiceLoader<Animals> load = ServiceLoader.load(Animals.class);  
for (Animals animals1 : load) {  
	animals1.call();  
}

SpringBoot(Spring)提供的SPI

在目录resource/META-INF下创建文件名为spring.factories的文件,文件内容为各个接口的全限定名及其实现类,以键值对的形式,一个文件中可以有多个接口及其多个实现类的定义。多个实现类之间以英文逗号分隔

spring.factories文件内容,键是接口全限定名,值为其实现类的全限定名,逗号分隔多个实现类。

txt
com.ecjtu.api.Animals=\  
com.ecjtu.api.Dog,\  
com.ecjtu.api.Cat

使用SpringFactoriesLoader来加载接口的实现类

java
List<Animals> animals = SpringFactoriesLoader.loadFactories(Animals.class, null);  
for (Animals animal : animals) {  
	animal.call();  
}

使用SpringBoot的自动配置机制实现一个自定义线程池的starter

需要的外部依赖

pom
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
		<version>2.7.17</version>
		<optional>true</optional>
	</dependency>
	<!-- 可以不要,用来做参数校验的一个依赖 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-validation</artifactId>
		<version>2.7.17</version>
		<optional>true</optional>
	</dependency>
</dependencies>
<build>
	<finalName>${project.artifactId}</finalName>
	<resources>
		<resource>
			<directory>src/main/resources</directory>
			<includes>
				<include>*/**</include>
			</includes>
		</resource>
	</resources>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<version>2.7.17</version>
		</plugin>
	</plugins>
</build>

步骤

  • 第一步:新建一个工程custom-thread-pool-spring-boot-starter。
  • 第二步:创建自动配置类。
java
@Configuration
@ConditionalOnClass(ThreadPoolExecutor.class)//存在此类时才进行自动配置
@EnableConfigurationProperties({ThreadPoolProperties.class})//导入线程池属性配置类
public class ThreadPoolAutoConfiguration {
    private static final Logger log = LoggerFactory.getLogger(ThreadPoolAutoConfiguration.class);
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(@Autowired ThreadPoolProperties properties){
        log.info("ThreadPoolExecutor --> 进行自动配置");
        return new ThreadPoolExecutor(
                properties.getCorePoolSize(),
                properties.getMaximumPoolSize(),
                properties.getKeepAliveTime(),
                properties.getUnit(),
                new ArrayBlockingQueue<>(properties.getWorkQueueSize())
                );
    }
}
  • 第三步:创建ThreadPoolProperties.class用于从yml文件中读取线程池初始化参数。

是通过setter方法进行的注入

java
@ConfigurationProperties("spring.custom.thread-pool")
public class ThreadPoolProperties {
    /*
    核心线程数
     */
    @Range(min = 0,max = Integer.MAX_VALUE,message = "核心线程数量超出范围")
    private int corePoolSize;
    /*
    最大线程数
     */
    @Range(min = 1,max = Integer.MAX_VALUE,message = "最大线程数超出范围")
    private int maximumPoolSize;
    /*
    存活时间
     */
    @Range(min = 0l,max = Long.MAX_VALUE)
    private long keepAliveTime;
    /*
    时间单位
     */
    @NotNull
    private TimeUnit unit;
    /*
    任务队列大小,默认任务队列为ArrayBlockingQueue
     */
    @Range(min = 1,max = Integer.MAX_VALUE)
    private int workQueueSize;
    public ThreadPoolProperties() {
    }

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public int getMaximumPoolSize() {
        return maximumPoolSize;
    }

    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    public long getKeepAliveTime() {
        return keepAliveTime;
    }

    public void setKeepAliveTime(long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
    }

    public TimeUnit getUnit() {
        return unit;
    }

    public void setUnit(TimeUnit unit) {
        this.unit = unit;
    }

    public int getWorkQueueSize() {
        return workQueueSize;
    }

    public void setWorkQueueSize(int workQueueSize) {
        this.workQueueSize = workQueueSize;
    }
}
  • 第四步:创建自定义类型转换器,将字符串类型转换为 TimeUnit 枚举类型。
java
@Component
@ConfigurationPropertiesBinding//声明注册此转换器
public class TimeUnitConverter implements Converter<String, TimeUnit> {
    @Override
    public TimeUnit convert(String source) {
        TimeUnit t = null;
        if(source.equals("SECONDS") || source.equals("seconds")){
            t = TimeUnit.SECONDS;
        }else if(source.equals("HOURS") || source.equals("hours")){
            t = TimeUnit.HOURS;
        }else if(source.equals("DAYS") || source.equals("days")){
            t = TimeUnit.DAYS;
        }else if(source.equals("MICROSECONDS") || source.equals("microseconds")){
            t = TimeUnit.MICROSECONDS;
        }else if(source.equals("MILLISECONDS") || source.equals("milliseconds")){
            t = TimeUnit.MILLISECONDS;
        }else if(source.equals("NANOSECONDS") || source.equals("nanoseconds")){
            t = TimeUnit.NANOSECONDS;
        }else if(source.equals("MINUTES") || source.equals("minutes")){
            t = TimeUnit.MINUTES;
        }
        return t;
    }
}
  • 第五步:新建文件resource/META-INF/spring.factories 文件内容
xml
# 指定自定义线程池的自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
com.ecjtu.autoconfigure.ThreadPoolAutoConfiguration

至此已经完成,在需要使用此线程池的地方导入该项目依赖,并在yml文件中配置相应参数即可使用SpringBoot的自动注入功能注入此线程池对象。

yml
# 配置示例
spring:
  custom:
    thread-pool:
      corePoolSize: 20
      maximumPoolSize: 20
      keepAliveTime: 20
      unit: SECONDS
      workQueueSize: 5

小结

Spring提供的SPI比Java提供的更加方便,不需要多个文件,一个文件即可定义全部接口及其实现类。都可以实现根据定义的bean信息并实例化。SpringBoot自动配置也是通过扫描spring.factories文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration的值来注入自动配置类进行自动配置(自动加载配置类)。

代码示例仓库

C++
通过自定义注解向Spring容器中注入个性化Bean
Valaxy v0.18.5 驱动 | 主题 - Yun v0.18.5
本站总访问量
本站访客数 人次
本站已运行0 天0 小时0 分0 秒后缀