Spring 引导组件扫描上下文
Spring Boot Component Scan Context
我有 10 多个 Spring 引导应用程序,它们可以构建为 war 文件并部署在应用程序服务器中,或者 运行 单独作为独立应用程序。每个应用程序都包含独特的代码、功能和业务运营所需的 RESTful 服务。
我的包结构如下:
WebServices (Gradle project)
|---A (Gradle project)
|---B (Gradle project)
|---C (Gradle project)
服务 A、B 和 C 都已打包,并且 运行可作为 wars。
但是,我还想提供启动一个包含所有服务的 "large" 服务器的选项,而不必单独启动每个应用程序或使主应用程序服务器因大型 Spring 开机 wars.
理想情况下,这将通过另一个 Spring 启动应用程序,该应用程序利用 ComponentScan 来包含来自所有其他服务的功能。我打算让 Spring 引导应用程序 X 引用服务 A、B[=53= 中的功能], 和 C.
这个问题与上下文有关。我的每项服务在通过应用程序服务器启动时,都会根据 war 文件的名称自动分配一个上下文。例如,如果我在应用程序 A 中具有安全功能,因为它包含我登录时使用的敏感信息:
/security/login
其中 security 是 war 文件 (security.war) 的名称。
为了在 运行 独立时弥补这一点,我为上下文设置了一个应用程序 属性 以匹配 war 文件名 server.servlet.context-path: /security
。这使我能够维持使用我的任一部署方法部署的相同端点天气。
现在,当我启动引用项目 A、B 和 X 的服务器时, C 与组件扫描 @ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})
我在应用程序 A 中丢失了 security 的上下文并且我的端点现在被访问为:
/login
应用程序 A、B 或 C 之间没有区别。
所以,我的问题是如何根据被扫描的组件维护单独的上下文,甚至基于上下文路由?
由于 spring 引导并不是真正针对您的要求而设计的,因此您必须非常有创意。以下是一些说明:
选项 1
只要你在同一台服务器上部署了很多 wars,contextPaths 就会不同,你可以根据实际服务器使用它们,但它们必须不同,以便网络服务器可以以某种方式区分它们。
考虑到这一点,您可以 运行 您想要做的事情,然后定义一个代理(Zuul、ha-proxy 或其他),它会仔细地将每个可能的 url 映射到适当的服务器。
选项 2
与 1 几乎相同,但您可以使用 docker-compose(甚至 kubernetes)和 运行 spring 启动应用程序作为不同的容器。如果您想在 CI 或开发中 运行 (docker compose 将根据创建的 war 文件的位置为您构建所有内容,这可以说是一个更好的解决方案,无需移动到 Web 服务器、部署服务器等)。只是说这是一个选项。
选项 3
如果您真的希望它们都在同一个内容路径中。创建一个 gradle 插件,将 wars / jars 合并为一个。胖罐风格。请注意,spring 启动应用程序可能具有不同的布局(对于 Jars 来说肯定是这样),因此实现可能应该像这样:
步骤 1
插件假定模块 A、B、C 已构建。现在我没有使用 Gradle 的经验,但在 Maven 中你可以转到目标目录(我相信 gradle 有一个用于相同目的的构建目录)并提取 war/jar (类, resources,dependencies) 到另一个文件夹,比如项目 A,然后对项目 B 执行相同的操作,依此类推。依赖项可能会发生冲突(不同的版本,因此您必须确保所有应用程序都使用相同版本的依赖项来消除问题),类 不应发生冲突,因为它们应该位于不同的包中(com.package.a
而不是 com.package.b
)。
步骤 2
在步骤 "a" 之后,您必须将所有内容打包到您选择的工件中,它应该仍然是一个 spring 引导项目。如果您选择 JAR,则必须了解它的构建方式。我无法对 WAR 发表评论,还没有与 spring boot.
中的那些人合作过
选项 4
与选项 3 几乎相同,但步骤 1 是在它构建的真实项目之一(如项目 A、B 等)时完成的。该插件会将生成的 类、依赖项或其他内容复制到某个预定义的文件夹中。该插件将安装在每个模块中,因此当它 运行 时,它将 "contribute" 到包含所有必需资源的文件夹中。步骤 2 与选项 3 相同。
更新
根据您的评论,考虑 Jars 和选项 1 / 2。Jars 是在 spring 引导应用程序中工作的推荐方式,wars 应该用于维护的老式组织Web 服务器,不想做 "Ops" 更改。
现在,如果端口有问题,如果您使用容器/kubernetes 编排,还有一个选项可以为每个服务分配一个虚拟主机名,这样所有服务都可以在同一个端口上访问,但可以使用虚拟主机姓名。代理选项将允许所有端口和主机相同
我用另一种方式解决了这个问题。
我在每个单独的控制器上设置了一个默认值 RequestMapping("${serviceContext}")
。每个控制器的 service 发生变化。即 aContext、bContext 和 cContext.
在我已经被上下文绑定的环境中,在一个应用程序服务器中并且通过 application.yml 独立,这个 属性 没有设置并且导致已经存在的绑定。
在我的捆绑应用程序中,我能够保留 @ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})
的组件扫描。然后更改变成在应用程序启动期间添加其他属性。
Properties used to bind context
Properties properties = new Properties();
properties.put("aContext", "/security");
properties.put("bContext", "/b");
properties.put("cContext", "/c");
我在 运行 应用程序之前将其添加到 Spring 的 setDefaultProperties
。然后当我将每个作为一个包启动时,我得到了预期的上下文绑定。
我有 10 多个 Spring 引导应用程序,它们可以构建为 war 文件并部署在应用程序服务器中,或者 运行 单独作为独立应用程序。每个应用程序都包含独特的代码、功能和业务运营所需的 RESTful 服务。
我的包结构如下:
WebServices (Gradle project)
|---A (Gradle project)
|---B (Gradle project)
|---C (Gradle project)
服务 A、B 和 C 都已打包,并且 运行可作为 wars。
但是,我还想提供启动一个包含所有服务的 "large" 服务器的选项,而不必单独启动每个应用程序或使主应用程序服务器因大型 Spring 开机 wars.
理想情况下,这将通过另一个 Spring 启动应用程序,该应用程序利用 ComponentScan 来包含来自所有其他服务的功能。我打算让 Spring 引导应用程序 X 引用服务 A、B[=53= 中的功能], 和 C.
这个问题与上下文有关。我的每项服务在通过应用程序服务器启动时,都会根据 war 文件的名称自动分配一个上下文。例如,如果我在应用程序 A 中具有安全功能,因为它包含我登录时使用的敏感信息:
/security/login
其中 security 是 war 文件 (security.war) 的名称。
为了在 运行 独立时弥补这一点,我为上下文设置了一个应用程序 属性 以匹配 war 文件名 server.servlet.context-path: /security
。这使我能够维持使用我的任一部署方法部署的相同端点天气。
现在,当我启动引用项目 A、B 和 X 的服务器时, C 与组件扫描 @ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})
我在应用程序 A 中丢失了 security 的上下文并且我的端点现在被访问为:
/login
应用程序 A、B 或 C 之间没有区别。
所以,我的问题是如何根据被扫描的组件维护单独的上下文,甚至基于上下文路由?
由于 spring 引导并不是真正针对您的要求而设计的,因此您必须非常有创意。以下是一些说明:
选项 1
只要你在同一台服务器上部署了很多 wars,contextPaths 就会不同,你可以根据实际服务器使用它们,但它们必须不同,以便网络服务器可以以某种方式区分它们。 考虑到这一点,您可以 运行 您想要做的事情,然后定义一个代理(Zuul、ha-proxy 或其他),它会仔细地将每个可能的 url 映射到适当的服务器。
选项 2
与 1 几乎相同,但您可以使用 docker-compose(甚至 kubernetes)和 运行 spring 启动应用程序作为不同的容器。如果您想在 CI 或开发中 运行 (docker compose 将根据创建的 war 文件的位置为您构建所有内容,这可以说是一个更好的解决方案,无需移动到 Web 服务器、部署服务器等)。只是说这是一个选项。
选项 3
如果您真的希望它们都在同一个内容路径中。创建一个 gradle 插件,将 wars / jars 合并为一个。胖罐风格。请注意,spring 启动应用程序可能具有不同的布局(对于 Jars 来说肯定是这样),因此实现可能应该像这样:
步骤 1
插件假定模块 A、B、C 已构建。现在我没有使用 Gradle 的经验,但在 Maven 中你可以转到目标目录(我相信 gradle 有一个用于相同目的的构建目录)并提取 war/jar (类, resources,dependencies) 到另一个文件夹,比如项目 A,然后对项目 B 执行相同的操作,依此类推。依赖项可能会发生冲突(不同的版本,因此您必须确保所有应用程序都使用相同版本的依赖项来消除问题),类 不应发生冲突,因为它们应该位于不同的包中(com.package.a
而不是 com.package.b
)。
步骤 2
在步骤 "a" 之后,您必须将所有内容打包到您选择的工件中,它应该仍然是一个 spring 引导项目。如果您选择 JAR,则必须了解它的构建方式。我无法对 WAR 发表评论,还没有与 spring boot.
中的那些人合作过选项 4
与选项 3 几乎相同,但步骤 1 是在它构建的真实项目之一(如项目 A、B 等)时完成的。该插件会将生成的 类、依赖项或其他内容复制到某个预定义的文件夹中。该插件将安装在每个模块中,因此当它 运行 时,它将 "contribute" 到包含所有必需资源的文件夹中。步骤 2 与选项 3 相同。
更新
根据您的评论,考虑 Jars 和选项 1 / 2。Jars 是在 spring 引导应用程序中工作的推荐方式,wars 应该用于维护的老式组织Web 服务器,不想做 "Ops" 更改。
现在,如果端口有问题,如果您使用容器/kubernetes 编排,还有一个选项可以为每个服务分配一个虚拟主机名,这样所有服务都可以在同一个端口上访问,但可以使用虚拟主机姓名。代理选项将允许所有端口和主机相同
我用另一种方式解决了这个问题。
我在每个单独的控制器上设置了一个默认值 RequestMapping("${serviceContext}")
。每个控制器的 service 发生变化。即 aContext、bContext 和 cContext.
在我已经被上下文绑定的环境中,在一个应用程序服务器中并且通过 application.yml 独立,这个 属性 没有设置并且导致已经存在的绑定。
在我的捆绑应用程序中,我能够保留 @ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})
的组件扫描。然后更改变成在应用程序启动期间添加其他属性。
Properties used to bind context
Properties properties = new Properties(); properties.put("aContext", "/security"); properties.put("bContext", "/b"); properties.put("cContext", "/c");
我在 运行 应用程序之前将其添加到 Spring 的 setDefaultProperties
。然后当我将每个作为一个包启动时,我得到了预期的上下文绑定。