Spring Boot组件扫描上下文

时间:2019-12-05 04:56:08

标签: java spring spring-boot component-scan

我有10多个Spring Boot应用程序,可以将它们构建为war文件并部署在应用程序服务器中或单独运行。每个应用程序都包含业务操作所需的唯一代码,功能和RESTful服务。

我的包裹结构如下:

WebServices (Gradle project)
|---A (Gradle project)
|---B (Gradle project)
|---C (Gradle project)

服务A,B和C都打包并可以作为战争运行。

但是,我还希望提供一个选项来启动包含所有服务的“大型”服务器,而不必分别启动每个应用程序或因大型Spring Boot大战而停滞主应用程序服务器。

理想情况下,这将通过另一个Spring Boot应用程序进行,该应用程序使用ComponentScan包括所有其他服务的功能。我要使用Spring Boot应用程序 X ,该应用程序引用了服务 A B C 中的功能。 / p>

问题在于上下文。通过应用程序服务器启动时,我的每项服务都会根据war文件的名称自动分配一个上下文。例如,如果我在应用程序 A 中具有安全功能,因为它包含敏感信息,我可以使用以下方式登录:

  

/ 安全性 /登录

其中 security 是War文件(security.war)的名称。

为弥补这一点,在独立运行时,我为上下文设置了一个应用程序属性,以匹配war文件名server.servlet.context-path: /security。这使我可以保持使用两种部署方法部署的相同端点天气。

现在,当我启动服务器 X 时,该服务器引用带有组件的项目 A B C 扫描@ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})我在应用程序 A 中释放了安全性的上下文,现在可以通过以下方式访问我的端点:

  

/登录

应用程序 A B C 之间没有区别。

所以,我的问题是如何基于被扫描的组件来维护单个上下文,甚至基于上下文进行路由?

2 个答案:

答案 0 :(得分:1)

由于Spring Boot并非真正针对您的要求而设计,因此您必须具有很大的创造力。以下是一些指示:

选项1

只要在同一台服务器上部署了许多战争,contextPaths就会不同,您可以根据实际服务器使用它们,但是它们必须不同,以便Web服务器可以以某种方式区分它们。 考虑到这一点,您可以运行您要尝试执行的操作,然后定义一个代理(Zuul,ha-proxy或其他任何代理),该代理将仔细地将每个可能的URL映射到适当的服务器。

选项2

与1几乎相同,但是您可以使用docker-compose(甚至kubernetes)并将spring boot应用程序作为不同的容器运行。如果您想在CI或开发环境中运行,则可以说这可能是一个更好的解决方案(docker compose会在给定已创建war文件的位置的情况下为您构建所有内容,而无需移动到Web服务器,部署服务器等)。只是说这是一个选择。

选项3

如果您真的希望它们全部位于同一内容路径中。创建一个gradle插件,将wars / jars合并为一个。胖子风格。请注意,Spring Boot应用程序可能具有不同的布局(在Jars中肯定是这样),因此该实现可能应执行以下操作:

第1步

该插件假定已构建模块A,B,C。现在我没有Gradle的经验,但是在Maven中,您可以转到目标目录(我相信gradle中有一个用于相同目的的构建目录)并提取war / jar的内容(类,资源,依赖项)放入另一个文件夹,例如对于项目A,然后对项目B执行相同操作,依此类推。依赖项可能会发生冲突(不同版本,因此您必须确保所有应用程序都使用相同版本的依赖项来消除问题),这些类不应发生冲突,因为它们应驻留在不同的包中(com.package.acom.package.b相对)。

第2步

在步骤“ a”之后,您必须将所有内容打包到您选择的工件中,该工件仍应是spring boot项目。如果您选择使用JAR,则必须了解JAR的构建方式。我无法评论WAR,还没有在Spring Boot中与之合作。

选项4

与选项3几乎相同,但第1步是在其构建的真实项目之一(如项目A,B等)下完成的。该插件会将生产类,依赖项或其他内容复制到某个预定义的文件夹中。该插件将安装在每个模块中,以便它在运行时将“贡献”到一天结束时将包含所有必需资源的文件夹。步骤2与方法3相同。

更新

根据您的评论,考虑使用Jars和选项1 /2。Jars是在春季启动应用程序中推荐的工作方式,对于那些维护Web服务器并且不想执行“操作”的老式组织,应该使用wars ”更改。

现在,如果端口是一个问题,如果您使用容器/ kubernetes编排,则还可以为每个服务分配一个虚拟主机名,以便可以在同一端口上使用虚拟主机名访问所有虚拟主机名。代理服务器选项将为所有端口提供相同的端口和主机

答案 1 :(得分:0)

我以另一种方式解决了这个问题。

我在每个控制器上都设置了一个默认RequestMapping("${serviceContext}")。每个控制器的服务在何处更改。即 aContext bContext cContext

在我已被上下文绑定的环境中,在应用程序服务器内并通过application.yml独立运行,此属性未设置,并导致已经存在的绑定。

在捆绑软件应用程序中,我能够对@ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})进行组件扫描。然后,该更改将在应用程序启动期间添加其他属性。

  

用于绑定上下文的属性

Properties properties = new Properties();
properties.put("aContext", "/security");
properties.put("bContext", "/b");
properties.put("cContext", "/c");

我在运行应用程序之前将其添加到Spring的setDefaultProperties中。然后,当我将每个捆绑包启动时,我将获得预期的上下文绑定。