自定义插件加载器

自定义插件加载器-基于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
Spring Boot是一个用于构建Java企业级应用的开发框架,为开发者提供了自动配置、快速开发、便捷部署等功能。对于类加载的使用,Spring Boot提供了默认的类加载机制,但也可以通过自定义加载来实现一些特殊的需求。 自定义加载可以通过继承ClassLoader类来实现。在自定义加载中,我们可以重写findClass方法,在该方法中实现自己的类加载逻辑。例如,可以从特定的位置加载类文件,或者从其他资源中加载类。通过自定义加载,我们可以灵活地加载一些不在常规位置的类文件或资源。 在Spring Boot中,可以使用自定义加载来实现一些插件化的功能。例如,可以实现一个插件加载,负责加载插件模块,并将其实例化为Spring Bean。这样,在运行时我们可以动态地加载并使用一些自定义的功能模块,而不需要在编译时就将其包含在应用程序中。 另外,自定义加载还可以用于热部署功能的实现。通过定时或者其他方式,我们可以在运行时重新加载某些类文件,以实现代码的热更新,避免了重启应用程序的操作。 需要注意的是,使用自定义加载需要谨慎。不正确的使用或者滥用类加载可能会导致类冲突、内存泄漏等问题。因此,在自定义加载时,需要仔细考虑设计和实现,确保安全性和稳定性。 总之,Spring Boot提供了默认的类加载机制,但也支持自定义加载。通过自定义加载,我们可以实现一些特殊的需求,例如插件化功能和热部署功能。但在使用自定义加载时,需要谨慎思考和设计,确保安全性和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值