Plugin 的初始化流程分析 & Sofa 插件的使用

Plugin 的初始化流程分析

启动 soul-bootstrap 可以看到日志输出了加载的一些 plugin :

2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[global] [org.dromara.soul.plugin.global.GlobalPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[sign] [org.dromara.soul.plugin.sign.SignPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[waf] [org.dromara.soul.plugin.waf.WafPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[rate_limiter] [org.dromara.soul.plugin.ratelimiter.RateLimiterPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[hystrix] [org.dromara.soul.plugin.hystrix.HystrixPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[resilience4j] [org.dromara.soul.plugin.resilience4j.Resilience4JPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[divide] [org.dromara.soul.plugin.divide.DividePlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[webClient] [org.dromara.soul.plugin.httpclient.WebClientPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[divide] [org.dromara.soul.plugin.divide.websocket.WebSocketPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[alibaba-dubbo-body-param] [org.dromara.soul.plugin.alibaba.dubbo.param.BodyParamPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[sofa] [org.dromara.soul.plugin.sofa.SofaPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[dubbo] [org.dromara.soul.plugin.alibaba.dubbo.AlibabaDubboPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[monitor] [org.dromara.soul.plugin.monitor.MonitorPlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[response] [org.dromara.soul.plugin.sofa.response.SofaResponsePlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[response] [org.dromara.soul.plugin.httpclient.response.WebClientResponsePlugin]
2021-01-18 22:14:10.685  INFO 71824 --- [           main] o.d.s.w.configuration.SoulConfiguration  : load plugin:[response] [org.dromara.soul.plugin.alibaba.dubbo.response.DubboResponsePlugin]

根据关键字 ‘load plugin’ 我们可以定位到 SoulConfiguration 这个配置类 的 @Bean(“webHandler”)

@Bean("webHandler")
    public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) { 
        List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
        final List<SoulPlugin> soulPlugins = pluginList.stream()
                .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
        soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
        return new SoulWebHandler(soulPlugins);
    }

那么为什么启动 soul-bootstrap 会去初始化 @Bean(“webHandler”) 这个Bean 呢?—- 不难发现,SoulConfiguration 类上面加了 组件扫描: @ComponentScan(“org.dromara.soul”),所以会扫描并初始化 org.dromara.soul 包下所有的 Bean

/** * SoulConfiguration. * * @author xiaoyu(Myth) */
@Configuration
@ComponentScan("org.dromara.soul")
@Import(value = { ErrorHandlerConfiguration.class, SoulExtConfiguration.class, SpringExtConfiguration.class})
@Slf4j
public class SoulConfiguration { ...}

我们再来看 soul-bootstrap 的代码,soul-bootstrap 里定义了 bean(在SoulNettyWebServerFactory类),通过初始化这个 bean 来创建了一个 Netty Server。

    /** * Init SoulWebHandler. * * @param plugins this plugins is All impl SoulPlugin. * @return {@linkplain SoulWebHandler} */
    @Bean("webHandler")
    public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) { 
        List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
        final List<SoulPlugin> soulPlugins = pluginList.stream()
                .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
        soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
        return new SoulWebHandler(soulPlugins);
    }

那么 soul-bootstrap 启动日志里打印的 10 多个 plugin 是何时初始化好的呢?

带着这个问题,我在创建 Netty Server 的地方加了一个断点,然后查看 beanFactory

在 beanFactory 我们可以看到 Spring 已经装载了很多插件的配置类。以 SofaPluginConfiguration 为例,我们可以发现,每一个插件都自定了一个 starter,starter 的 spring.factories 文件里都指定了 自动配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.soul.spring.boot.starter.plugin.sofa.SofaPluginConfiguration

而 soul-bootstrap 的 pom 文件里也引入了这些插件的starter依赖,详见 soul-bootstrap 的pom文件,这里只贴出来 divide 插件的 starter 依赖:

		<!--if you use http proxy start this-->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-plugin-divide</artifactId>
            <version>${ project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId>
            <version>${ project.version}</version>
        </dependency>
        <!--if you use http proxy end this-->

所以为什么 sofa 插件跑起来后,请求不会转发到 soul-examples-sofa 这个项目?因为 soul-bootstrap 的 pom 文件里是没有 sofa 的starter 依赖,也就无法将 sofa 插件这个bean初始化,要解决这个问题,只要在 soul-bootstrap 的 pom 文件里加入 sofa 的starter依赖即可。

sofa starter依赖:

		<dependency>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>sofa-rpc-all</artifactId>
            <version>5.7.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-plugin-sofa</artifactId>
            <version>${ project.version}</version>
        </dependency>

因为每个 Plugin 都实现了 SoulPlugin 这个类(相当于都是 SoulPlugin 类型),并且每个Plugin做为一个个的bean都已经自动初始化好,所以这里可以用 List 来接收找出来的所有 Plugin beans。

	@Bean("webHandler")
    public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) { 
        List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
        final List<SoulPlugin> soulPlugins = pluginList.stream()
                .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
        soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
        return new SoulWebHandler(soulPlugins);
    }

Sofa 插件的使用

在 soul-bootstrap 的 pom 文件里加上了 sofa 的 starter 依赖,启动 soul-admin,soul-bootstrap 以及 example 下的 sofa-test,访问 http://localhost:9195/sofa/findAll,便可以成功访问到 sofa-test 了:

总结

  1. 通过在自定义 starter 的 spring.factories 文件中开启 AutoConfig,可以帮助主项目(soul-bootstrap)以外的bean(即在pom文件中添加依赖中的bean)装载到 spring 容器,从而实现启动 bootstrap 时将插件都初始化好。

本文地址:https://blog.csdn.net/wu2304211/article/details/112798222