自定义插件加载器-基于SPI
参考 dolphinscheduler
定义插件工厂
@Data
@AllArgsConstructor
@Builder
public class PluginIdentify {
private static final int DEFAULT_PRIORITY = 0;
/**
* 名称
*/
private String name;
@Builder.Default
private int priority = DEFAULT_PRIORITY;
}
/**
* @Date: 2024/9/11 10:08
* 具有优先级的插件
*/
public interface PluginSPI extends Comparable<Integer> {
String pluginName();
default PluginIdentify getIdentify(){
return PluginIdentify.builder().name(pluginName()).build();
}
@Override
default int compareTo(Integer o) {
return Integer.compare(getIdentify().getPriority(), o);
}
}
/**
* @Date: 2024/9/11 10:20
* 插件工厂
*/
public interface PluginFactory<P> extends PluginSPI {
/**
* 插件创建
*
* @return
*/
P create();
}
插件工厂 SPI 加载器
/**
* 插件工厂 SPI 加载器
* 根据优先级
* @param <T>
*/
@Slf4j
public class PrioritySPIFactory<T extends PluginSPI> {
private final Map<String, T> map = new HashMap<>();
public PrioritySPIFactory(Class<T> spiClass) {
for (T t : ServiceLoader.load(spiClass)) {
if (map.containsKey(t.getIdentify().getName())) {
resolveConflict(t);
} else {
map.put(t.getIdentify().getName(), t);
}
}
}
public Map<String, T> getSPIMap() {
return Collections.unmodifiableMap(map);
}
private void resolveConflict(T newSPI) {
PluginIdentify identify = newSPI.getIdentify();
T oldSPI = map.get(identify.getName());
if (newSPI.compareTo(oldSPI.getIdentify().getPriority()) == 0) {
throw new IllegalArgumentException(
String.format("These two spi plugins has conflict identify name with the same priority: %s, %s",
oldSPI.getIdentify(), newSPI.getIdentify()));
} else if (newSPI.compareTo(oldSPI.getIdentify().getPriority()) > 0) {
log.info("The {} plugin has high priority, will override {}", newSPI.getIdentify(), oldSPI);
map.put(identify.getName(), newSPI);
} else {
log.info("The low plugin {} will be skipped", newSPI);
}
}
}
插件管理器
public class PluginManager<P,F extends PluginFactory<P>> {
/**
* 插件工厂
*/
private Class<F> factory;
private final Map<String,P> pluginMap = new ConcurrentHashMap<>();
private final Map<String,F> pluginFactoryMap = new ConcurrentHashMap<>();
public PluginManager(Class<F> factory) {
this.factory = factory;
}
/**
* 加载插件
*/
public void loadPlugin(){
PrioritySPIFactory<F> prioritySPIFactory = new PrioritySPIFactory<F>(factory);
for (Map.Entry<String, F> entry : prioritySPIFactory.getSPIMap().entrySet()) {
pluginMap.put(entry.getKey(), entry.getValue().create());
pluginFactoryMap.put(entry.getKey(),entry.getValue());
}
}
/**
* 获取插件列表
* @return
*/
public Map<String, P> getPlugins() {
return Collections.unmodifiableMap(pluginMap);
}
/**
* 根据插件名称获取插件
* @param pluginName
* @return
*/
public Optional<P> getPlugin(String pluginName) {
return Optional.ofNullable(getPlugins().get(pluginName));
}
/**
* 获取插件工厂
* @return
*/
public Map<String, F> getPluginFactories() {
return Collections.unmodifiableMap(pluginFactoryMap);
}
/**
* 根据插件名称获取插件
* @param pluginName
* @return
*/
public Optional<F> getPluginFactory(String pluginName) {
return Optional.ofNullable(getPluginFactories().get(pluginName));
}
}
举例
任务工厂
public interface TaskChannel {
/**
* 执行
*/
void execute();
}
//任务工厂
public interface TaskChannelFactory extends PluginFactory<TaskChannel> {
String config();
}
实现
public class HttpChannel implements TaskChannel {
@Override
public void execute() {
System.out.println("http plugin execute");
}
}
@AutoService(TaskChannelFactory.class)
public class HttpTaskChannelFactory implements TaskChannelFactory{
@Override
public TaskChannel create() {
return new HttpChannel();
}
@Override
public String pluginName() {
return "http";
}
@Override
public String config() {
return "http config";
}
}
public class ShellChannel implements TaskChannel {
@Override
public void execute() {
System.out.println("shell execute");
}
}
@AutoService(TaskChannelFactory.class)
public class ShellTaskChannelFactory implements TaskChannelFactory {
@Override
public TaskChannel create() {
return new ShellChannel();
}
@Override
public String pluginName() {
return "shell";
}
@Override
public String config() {
return "shell config";
}
}
测试
public class PluginManagerMain {
public static void main(String[] args) {
PluginManager<TaskChannel, TaskChannelFactory> pluginManager = new PluginManager<>(TaskChannelFactory.class);
pluginManager.loadPlugin();
Optional<TaskChannel> http = pluginManager.getPlugin("http");
http.ifPresent(TaskChannel::execute);
Optional<TaskChannel> shell = pluginManager.getPlugin("shell");
shell.ifPresent(TaskChannel::execute);
}
}
结果
http plugin execute
shell execute