Giter Club home page Giter Club logo

Comments (6)

xinkunZ avatar xinkunZ commented on July 17, 2024

以及 PluginListableBeanFactory 未传递插件平台的parentBeanFactory,导致插件的feign无法拿到插件平台的load balance

from sbp.

hnzhrh avatar hnzhrh commented on July 17, 2024

@xinkunZ 大哥能参考下你的代码吗?我也需要做类似这样的需求。。。头大了

from sbp.

xinkunZ avatar xinkunZ commented on July 17, 2024

@hnzhrh 思路:用annotation application context作为插件的spring context,将插件平台的spring context作为插件的parent,使得插件可以优先从parent中获取bean。 以及,考虑自定义插件的application context的classloader,在加载class时优先从平台(即启动插件的线程的上下文classloader)加载class,找不到再从插件jar( PluginClassLoader )加载class

  • 插件start逻辑
        applicationContext = new AnnotationConfigApplicationContext();
        final String home = StringUtils.substringAfterLast(homePackage, ".");
        applicationContext.setBeanNameGenerator(
                new PluginBeanNameGenerator(StringUtils.equals(home, "plugin") ? null : home));
        ConfigurationPropertiesBindingPostProcessor.register(applicationContext);
        applicationContext.setEnvironment(prepareEnvironment());
        applicationContext.setParent(this.getMainApplicationContext());
        applicationContext.setClassLoader(wrapper.getPluginClassLoader());
        applicationContext.scan(homePackage);
        applicationContext.refresh();
  • 自定义的classloader
public static class MyClassLoader extends PluginClassLoader {

        private final ClassLoader current;

        MyClassLoader(final PluginManager pluginManager, final PluginDescriptor pluginDescriptor,
                      final ClassLoader parent) {
            super(pluginManager, pluginDescriptor, parent);
            this.current = parent;
        }

        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            try {
                Class<?> aClass = current.loadClass(name);
                if (aClass != null) {
                    return aClass;
                }
            } catch (ClassNotFoundException e) {
                return super.loadClass(name);
            }
            throw new ClassNotFoundException(name);
        }
    }

from sbp.

hank-cp avatar hank-cp commented on July 17, 2024

抛开情绪性的内容, 你实际上提出了两个很好的问题:

  1. 为什么基于SpringBoot封装, 而不是直接基于Spring封装一套插件框架, 为什么要费么那大劲去介入Spring Boot的启动过程? 首先基于Spring的pf4j封装, pf4j作者decebals已经提供了pf4j-spring. 同时他也回答过, 他不是一位Java后台开发人员, 对Spring Boot没有开发经验, 无意提供基于Spring Boot的pf4j封装, 所以这才有了sbp的诞生. Spring Boot跟spring-core的区别. 其中最重要最有实现意义的一点, 在于通过properties完成各种中间件组件的集成配置. 比如Datasource, Jpa, Jackson等等几百个库, 上千项配置. sbp这样设计可以实现每个插件的配置独立或共享, 每个插件可以被独立启动以进行自动化测试. 如果应用场景足够简单, 使用spring-core+手工配置即可解决, 那确实无需使用到Spring Boot, 更无需用到sbp.

  2. 为什么要去折腾ClassLoader控制类加载的顺序? 当初是为了适应Jpa的插件化, 这在文档中有详细解释. 在最新版本的sbp中, Jps(Hibernate)的插件化集成方式改变了, 因此也不再需要用到这个配置项.

你提到的0.1.5版本是2020年旧版本了. sbp在2023年初为了支持spirng boot 3.0和webflux, 进行了一波大重构. 你也可以了解一下.

最后我想说的是. sbp从一开始就有明确的实现目标, 也有比较完整的单元测试覆盖率保证其提供符合预期的功能. 如果在同一实现目标的前提下, 有更好的实现方式, 也欢迎随时提出问题和改进建议. 目前sbp这个项目我利用个人空闲时间用爱发电已经维持了5年时间, 已在多个真实项目中使用验证过, 但目前的状态还是一个比较抽象的底层框架, 使用起来确实仍有不低的门槛, 目前比较成熟的应用场景是提供api服务, 并利用插件化的能力向不同的部署目标提供差异化交付. 我一直希望能够推出像其它"Spring微服务全家桶"那样开箱即用的模板工程, 但迟迟没有找到一个比较完美的前端插件化方案配合. 希望有时间有能力的朋友能加入一起玩.

from sbp.

xinkunZ avatar xinkunZ commented on July 17, 2024

好了我冷静完了 先道个歉。然后说一下我对插件的设计思路

我以为,IDEA有自己的插件、eclipse有自己的插件,因此,我们设计的插件系统应该是针对一个指定的服务(插件平台),而为这个服务开发的插件才可以被这个服务加载,插件大部分的依赖、spring bean都应该由平台提供。

常见用例: 平台提供一个interface class,而对此平台开发的插件,可以实现此接口,注册成bean,平台循环所有的插件的context,以上述interface类名拿到所有实现,已达到扩充平台端能力的目的。

那么,因此: 如果插件是springboot,那么引申而来的autoconfig、容器端口等问题过于复杂,并且这样做的意义不明: 既然插件已经是springboot,那为什么不单独启动(docker、jar)

因此,我设计的插件系统的目标:

  1. 插件系统有两个依赖: 平台api和插件api。平台api是给springboot工程使用,插件api是给插件使用(包含基于p4j的自定义spring plugin父类)
  2. 插件能够继承平台的spring context(反之见第三点)、可以拿到平台的所有bean;平台可以拿到某插件id的contexct、也可以拿到所有插件的context、各插件的context独立且bean不会冲突
  3. 允许插件注册bean到平台的context。典型场景:controller。即,允许插件扩充controller给平台,以实现新的reset接口
  4. 插件所有的能力项基本来自平台,因为context继承的缘故,插件甚至可以无缝使用包括但不限于:平台定义好的数据库、service、mq等等

from sbp.

hank-cp avatar hank-cp commented on July 17, 2024

那么,因此: 如果插件是springboot,那么引申而来的autoconfig、容器端口等问题过于复杂,并且这样做的意义不明: 既然插件已经是springboot,那为什么不单独启动(docker、jar)

正是因为AutoConfiguration复杂, 所以才要让SpringBoot帮我们去做好这些乱七八糟的鸟事. 就拿你提的方案里面第3点举例, 如果不用SpringBoot去配置Controller, 那么就要自己配置aop, 配置security, 配置TransactionManager, 配置ConversionService等等各种看不见但是离不开的东西, 这些工作量都不少的.

基于spring框架的插件化, 其实问题不在于用spring还是spring boot, 还是要根据项目情况和个人习惯去做选择.

插件化的难点其实是在于对各个现在中间件库的插件化改造. 目前主要的中间件库实现基本上都是只考虑在启动时完成配置就用到死, 没留下多少能在运行时更改配置的接口, 很多时间只能通过反射hack掉或整个context重新加载.

from sbp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.