记录一次JDK SPI的使用,并能在Service Loader加载实现类后将其注入到Spring容器中,如此可以在实现类中使用自动注入其他依赖等。
Service Provider Interface
SPI工程目录大致如下,基于Spring Boot搭建,使用到Lombok(可视情况去除):
src
└─com
└─ym
└─demo
└─spi
├─ extra
│ ├─ SPIBeanAutoWire.java
│ └─ SpringUtil.java
│
├─ service
│ └─ DemoSPIService.java
│
├─ DemoManager.java
└─ ServiceLoaderHelper
SPI对外开放接口类
需要在使用包定义实现类,并且按照ServiceLoader约定标准文件方式以该类的全类名(包名+类名)新建文件
public interface DemoManager {
Object someMethod(String param);
}
SPI的实际使用Service
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
//使用懒加载保证实现类加载
@Lazy
@Service
@Slf4j
public class DemoSPIService {
private final DemoManager demoManager;
public DemoSPIService() {
demoManager = ServiceLoaderHelper.loadSingleService(DemoManager.class, true);
}
public Object someMethod(String param) {
if (demoManager == null) {
log.error("未发现 DemoManager 服务提供者");
return null;
}
return demoManager.someMethod(param);
}
}
ServiceLoader的工具类
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
public class ServiceLoaderHelper {
public static <T> List<T> loadServices(Class<T> spiClazz) {
ServiceLoader<T> serviceLoader = ServiceLoader.load(spiClazz);
Iterator<T> itr = serviceLoader.iterator();
List<T> services = new ArrayList<T>();
while (itr.hasNext()) {
services.add(itr.next());
}
return services;
}
public static <T> T loadSingleService(Class<T> spiClazz) {
return loadSingleService(spiClazz, false);
}
/**
* 加载单个服务
*
* @param allowNoService 支持无实现可运行
*/
public static <T> T loadSingleService(Class<T> spiClazz, boolean allowNoService) {
ServiceLoader<T> serviceLoader = ServiceLoader.load(spiClazz);
Iterator<T> itr = serviceLoader.iterator();
T service = null;
while (itr.hasNext()) {
if (service == null) {
service = itr.next();
} else {
StringBuilder sb = new StringBuilder(service.getClass().getName());
while (itr.hasNext()) {
sb.append(",").append(itr.next().getClass().getName());
}
// 这里需要结合实际情况进行处理,可以自己定义SystemConfigurationException继承RuntimeException
throw new SystemConfigurationException("配置了多个" + spiClazz + "的实现,包括:" + sb.toString());
}
}
//增加allowNoService支持
if (service == null && !allowNoService) {
throw new SystemConfigurationException("未配置" + spiClazz + "的实现。");
}
//判断是否自动注入Spring容器
if (service instanceof SPIBeanAutowire) {
AutowireCapableBeanFactory beanFactory = SpringUtil.getApplicationContext().getAutowireCapableBeanFactory();
beanFactory.autowireBeanProperties(service, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
}
return service;
}
}
用来判断是否注入Spring容器的接口类
public interface SPIBeanAutowire {
}
Spring容器工具类
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public SpringUtil() {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
SPI某服务使用示例
@Service
public class SomeService {
//使用懒加载保证实现类加载
@Lazy
@Autowired
private DemoSPIService demoSPIService;
public void someServiceMethod(String param) {
demoSPIService.someMethod(param);
}
}
Service Provider
SPI的使用及对应实现工程。
SPI的实现类
public class DemoManagerImpl implements DemoManager, SPIBeanAutowire {
@Autowired
private OtherService otherService;
@Override
public Object someMethod(String param) {
return otherService.anotherMethod(param);
}
}
ServiceLoader的约定文件
文件路径:src/META-INF/services/
文件名:com.ym.demo.spi.DemoManager(该处为示例,取:SPI 的全类名)
文件内容:com.xxx.xxx.DemoManagerImpl(该处为示例,取:SPI实现类的全类名)