简体   繁体   English

Spring Boot 组件扫描上下文

[英]Spring Boot Component Scan Context

I have 10+ Spring Boot applications that can be built as war files and deployed within and application server, or run individually as standalone.我有 10 多个 Spring Boot 应用程序,它们可以构建为 war 文件并部署在应用程序服务器中,也可以单独运行。 Each application contains unique code, functionality, and RESTful services required for business operations.每个应用程序都包含业务运营所需的唯一代码、功能和 RESTful 服务。

My package structure is as follows:我的包结构如下:

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

Services A, B, and C are all packaged and runnable as wars.服务 A、B 和 C 都打包并可以作为战争运行。

However, I also want to provide the option to start up one "large" server that contains all the services without having to start each application separately or bogging down the primary application server with the large Spring Boot wars.但是,我还想提供启动一个包含所有服务的“大型”服务器的选项,而不必单独启动每个应用程序或因大型 Spring Boot 战争而使主应用程序服务器陷入困境。

Ideally this was going to be via another Spring Boot application that utilized the ComponentScan to include functionality from all other services.理想情况下,这将通过另一个 Spring Boot 应用程序使用 ComponentScan 来包含所有其他服务的功能。 I was going to have Spring Boot application X that referenced functionality within services A , B , and C .我打算让 Spring Boot 应用程序X引用服务ABC 中的功能

The issue with this is with context.这个问题与上下文有关。 Each of my services, when started via an application server, are automatically assigned a context based on the name of the war file.我的每个服务在通过应用程序服务器启动时,都会根据 war 文件的名称自动分配一个上下文。 For example, if I have security functionality in application A since it contains sensitive information I log in with:例如,如果我在应用程序A 中具有安全功能,因为它包含我登录的敏感信息:

/ security /login /安全/ 登录

Where security is the is the name of the war file (security.war).其中security是 war 文件 (security.war) 的名称。

To compensate for this while running standalone I have an application property set for the context to match the war file name server.servlet.context-path: /security .为了在独立运行时对此进行补偿,我为上下文设置了一个应用程序属性以匹配战争文件名server.servlet.context-path: /security This allows me to maintain the same endpoint weather deployed with either of my deployment methods.这使我可以维护使用我的任一部署方法部署的相同端点天气。

Now, when I start server X that references projects A , B , and C with the component scan @ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"}) I loose my context of security within application A and my endpoint now is accessed as:现在,当我启动引用项目ABC 的服务器X 时,组件扫描@ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})我在应用程序A 中丢失了我的安全上下文,我的端点现在被访问为:

/login /登录

There is no differentiation between applications A , B , or C .应用程序ABC之间没有区别。

So, my question is how can I maintain individual contexts, or even route based on context, based on the components being scanned?所以,我的问题是如何根据正在扫描的组件维护单个上下文,甚至基于上下文的路由?

Since spring boot is not really designed for what you're asking, you'll have to be very creative.由于弹簧靴并不是真正为您的要求而设计的,因此您必须非常有创意。 Here are some directions:以下是一些方向:

Option 1选项1

As long as you have many wars deployed at the same server, the contextPaths will be different, you can play with them depending on the actual server but they have to you be different so that web server could differentiate between them somehow.只要您在同一台服务器上部署了许多战争,contextPaths 就会不同,您可以根据实际服务器使用它们,但它们必须与您不同,以便 Web 服务器可以以某种方式区分它们。 With that in mind you can run what you're trying to do but then define a proxy (Zuul, ha-proxy or whatever) that would carefully map every possible url to the appropriate server.考虑到这一点,您可以运行您正在尝试执行的操作,然后定义一个代理(Zuul、ha-proxy 或其他),它将仔细地将每个可能的 url 映射到适当的服务器。

Option 2选项 2

Pretty much the same as 1, but you can use docker-compose (or even kubernetes) and run spring boot applications as different containers.与 1 几乎相同,但您可以使用 docker-compose(甚至 kubernetes)并将 Spring Boot 应用程序作为不同的容器运行。 This arguably might be somewhat a better solution if you want to run in CI or development (docker compose will build everything for you given the locations of the created war files, no need to move to web server, deploy the server, etc).如果您想在 CI 或开发中运行,这可以说是更好的解决方案(docker compose 将根据创建的战争文件的位置为您构建所有内容,无需移动到 Web 服务器、部署服务器等)。 Just stating this as an option.只是将其声明为一个选项。

Option 3选项 3

If you really want them all in the same content path.如果您真的希望它们都在相同的内容路径中。 Create a gradle plugin that would merge the wars / jars into one.创建一个 gradle 插件,将战争/罐子合并为一个。 Fat-Jar style. Fat-Jar 风格。 Note that spring boot applications might have a different layout (Its for sure in case of Jars), so the implementation should probably do something like this:请注意,spring boot 应用程序可能具有不同的布局(在 Jars 的情况下肯定是这样),因此实现可能应该执行以下操作:

Step 1第1步

The plugin assumes that the modules A,B,C are built.该插件假定模块 A、B、C 已构建。 Now I don't have experience with Gradle but in Maven you could go to the target directory (I believe in gradle there is a build directory for the same purpose) and extract the content of the war/jar (classes, resources,dependencies) into another folder, say for project A, then do the same for project B, and so forth.现在我没有使用 Gradle 的经验,但在 Maven 中,您可以转到目标目录(我相信 gradle 有一个用于相同目的的构建目录)并提取 war/jar 的内容(类、资源、依赖项)进入另一个文件夹,比如项目 A,然后对项目 B 执行相同的操作,依此类推。 The dependencies might clash (different versions, so you'll have to make sure that all the applications use the same versions of dependencies to eliminate the issues), the classes should not clash since they should reside in different packages ( com.package.a as opposed to com.package.b ).依赖项可能会发生冲突(不同版本,因此您必须确保所有应用程序都使用相同版本的依赖项来消除问题),类不应发生冲突,因为它们应该驻留在不同的包中( com.package.acom.package.b相对)。

Step 2第2步

After step "a" you'll have to package everything into the artifact of your choice, that should be still a spring boot project.在步骤“a”之后,您必须将所有内容打包到您选择的工件中,这应该仍然是一个 spring 启动项目。 If you'll opt for JAR you'll have to understand how exactly its built.如果您选择 JAR,则必须了解它的构建方式。 I can't comment on WAR, haven't worked with those in spring boot.我无法对 WAR 发表评论,还没有与 Spring Boot 中的那些人一起工作。

Option 4选项 4

Pretty much the same as option 3 but step 1 is done when one of the real projects it built (like project A, B, etc).与选项 3 几乎相同,但在它构建的真实项目之一(如项目 A、B 等)时完成第 1 步。 The plugin would copy the produces classes, dependencies or whatever into some predefined folder.该插件会将生产类、依赖项或任何内容复制到某个预定义的文件夹中。 The plugin will be installed in each module so that when it runs it will "contribute" to the folder that would contain all the required resources at the end of the day.该插件将安装在每个模块中,以便在运行时它将“贡献”到包含所有所需资源的文件夹中。 Step 2 will be the same as in Option 3.步骤 2 将与选项 3 中的相同。

Update更新

Based on your comment, consider Jars and option 1 / 2. Jars are the recommended way to work in spring boot applications, wars should be used for old-fashioned organizations that maintain web servers and don't want to do "Ops" changes.根据您的评论,考虑 Jars 和选项 1 / 2。Jars 是在 Spring Boot 应用程序中推荐的工作方式,wars 应该用于维护 Web 服务器并且不想进行“Ops”更改的老式组织。

Now, if port is an issue, If you use containers / kubernetes orchestration, there is also an option to assign a virtual host name to each service, so that will all be accessible on the same port but with virtual host name.现在,如果端口是一个问题,如果您使用容器/kubernetes 编排,还有一个选项可以为每个服务分配一个虚拟主机名,这样所有服务都可以在同一个端口上访问,但使用虚拟主机名。 The option of proxy will allow same port and host for all of them代理选项将允许所有人使用相同的端口和主机

I resolved this in another manner.我以另一种方式解决了这个问题。

I placed a default RequestMapping("${serviceContext}") on each of my individual controllers.我在每个单独的控制器上放置了一个默认的RequestMapping("${serviceContext}") Where service changes for each controller.每个控制器的服务发生变化的地方。 ie aContext , bContext , and cContext .aContextbContextcContext

In my environments that are already bound by a context, within an application server and standalone via the application.yml, this property is not set and results in the already existing binding.在我已经由上下文绑定的环境中,在应用程序服务器中并通过 application.yml 独立,未设置此属性并导致已存在的绑定。

In my bundle application I was able to keep my component scan of @ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"}) .在我的捆绑应用程序中,我能够保持对@ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})组件扫描. The change then became adding additional properties during the startup of the application.然后,更改变成在应用程序启动期间添加其他属性。

Properties used to bind context用于绑定上下文的属性

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

I added this to Spring's setDefaultProperties before running the application.在运行应用程序之前,我将它添加到 Spring 的setDefaultProperties Then when I startup each as a bundle I get the expected context binding.然后,当我将每个作为一个包启动时,我会得到预期的上下文绑定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM