Spring Boot (JAR),具有多个调度程序 servlet,用于不同的 REST API,以及 Spring



我有一个项目,它使用 Spring Boot 生成一个可执行的 JAR,该 JAR 使用 Spring Data REST 公开了一个 REST API。它还与Spring Security OAuth集成。这工作正常。我的问题如下,

我想为 REST API 提供不同的模块,只有当具有 JPA 存储库的相应 JAR 位于类路径中(它已被定义为依赖项(时,我才希望启用这些模块。

问题是我希望他们彼此独立。我希望能够在具有不同映射的不同调度程序 servlet 下为它们提供服务,以便我可以为每个 baseUri 指定不同的 baseUri,并为资源发现提供不同的根 URL。

我将尝试使其更清楚:

  • API 的模块 A:

    • 一个 JAR,例如包含资源 X 和 Y 的 XRespository 和 YRespository。
    • 调度程序 servlet A。
    • Servlet mapping:/api/moduleA/
    • Spring Data REST 的基本 URI:/api/moduleA/
    • 如果我检查URL/api/moduleA/,我应该发现资源X和Y。
  • API 模块 B:

    • 一个 JAR,例如包含资源 P 和 Q 的 PRespository 和 QRespository。
    • 调度程序 servlet B。
    • Servlet mapping:/api/moduleB/
    • Spring Data REST 的基本 URI:/api/moduleB/
    • 如果我检查URL/api/moduleB/,我应该发现资源P和Q。
  • 更多模块...

除此之外,我可以有另一个调度程序 servlet,我在其中保存/oauth/* 端点以及其他自定义控制器,并且安全配置必须适用于所有 (/*(

我知道我可以通过 ServletRegistrationBean 定义更多的调度程序 servlet,但我不知道如何将不同的 spring data rest 配置附加到每个配置。

我也一直在尝试使用SpringApplicationBuilder的分层应用程序上下文来做到这一点,方法是在每个子上下文中具有定义每个调度程序servlet,每个RepositoryRestMvcConfiguration的配置,并让每个@EnableJpaRepositories注释定义要扫描的不同包。无论如何,我什至无法加载上下文,因为它们不是作为WebApplicationContext创建的,因此失败,因为没有可用的ServletContext。

有什么帮助/建议吗?提前谢谢。

我不久前找到了解决方案,但我忘了在这里分享,所以感谢 Jan 提醒我。

我通过创建和注册几个调度程序 servlet(具有不同配置的新 Web 应用程序上下文(RepositoryRestMvcConfiguration(和一个公共父级(Spring Boot 应用程序的根应用程序上下文(来解决它。为了根据类路径中包含的不同jar自动启用API模块,我模拟了Spring Boot或多或少的功能。

该项目分为几个 gradle 模块。像这样:

  • 项目服务器
  • 项目接口自动配置
  • 项目模块
  • 项目模块 B-API
  • 项目模块-n-API

模块项目服务器是主要的。它声明了对项目-api-autoconfigure的依赖关系,同时它排除了项目-api-autoconfigure对项目模块-?-api模块的传递依赖关系。

project-server.gradle中:

dependencies {
    compile (project(':project-api-autoconfigure')) {
        exclude module: 'project-module-a-api'
        exclude module: 'project-module-b-api'
        ...
    }
    ...
}

project-api-autoconfigure 依赖于所有 API 模块,因此在 project-api-autoconfigure.gradle 上,依赖项将如下所示:

dependencies {
    compile project(':project-module-a-api')
    compile project(':project-module-b-api')
    ...
}

project-api-autoconfigure 是我为每个 API 模块创建具有自己的 Web 应用程序上下文的调度程序 servlet bean 的地方,但这种配置取决于每个 API 模块 jar 中的每个 API 模块的配置类。

我创建并抽象了每个自动配置类继承的类:

public abstract class AbstractApiModuleAutoConfiguration<T> {
    @Autowired
    protected ApplicationContext applicationContext;
    @Autowired
    protected ServerProperties server;
    @Autowired(required = false)
    protected MultipartConfigElement multipartConfig;
    @Value("${project.rest.base-api-path}")
    protected String baseApiPath;
    protected DispatcherServlet createApiModuleDispatcherServlet() {
        AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        webContext.setParent(applicationContext);
        webContext.register(getApiModuleConfigurationClass());
        return new DispatcherServlet(webContext);
    }
    protected ServletRegistrationBean createApiModuleDispatcherServletRegistration(DispatcherServlet apiModuleDispatcherServlet) {
        ServletRegistrationBean registration = new ServletRegistrationBean(
                apiModuleDispatcherServlet,
                this.server.getServletMapping() + baseApiPath + "/" + getApiModulePath() + "/*");
        registration.setName(getApiModuleDispatcherServletBeanName());
        if (this.multipartConfig != null) {
            registration.setMultipartConfig(this.multipartConfig);
        }
        return registration;
    }
    protected abstract String getApiModuleDispatcherServletBeanName();
    protected abstract String getApiModulePath();
    protected abstract Class<T> getApiModuleConfigurationClass();
}

所以现在,模块 A 的自动配置类将如下所示:

@Configuration
@ConditionalOnClass(ApiModuleAConfiguration.class)
@ConditionalOnProperty(prefix = "project.moduleA.", value = "enabled")
public class ApiModuleAAutoConfiguration extends AbstractApiModuleAutoConfiguration<ApiModuleAConfiguration> {
    public static final String API_MODULE_A_DISPATCHER_SERVLET_BEAN_NAME = "apiModuleADispatcherServlet";
    public static final String API_MODULE_A_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "apiModuleADispatcherServletRegistration";
    @Value("${project.moduleA.path}")
    private String apiModuleAPath;
    @Bean(name = API_MODULE_A_DISPATCHER_SERVLET_BEAN_NAME)
    public DispatcherServlet apiModuleADispatcherServlet() {
        return createApiModuleDispatcherServlet();
    }
    @Bean(name = API_MODULE_A_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    public ServletRegistrationBean apiModuleADispatcherServletRegistration() {
        return createApiModuleDispatcherServletRegistration(apiModuleADispatcherServlet());
    }
    @Override
    protected String getApiModuleDispatcherServletBeanName() {
        return API_MODULE_A_DISPATCHER_SERVLET_BEAN_NAME;
    }
    @Override
    protected String getApiModulePath() {
        return apiModuleAPath;
    }
    @Override
    protected Class<ApiModuleAConfiguration> getApiModuleConfigurationClass() {
        return ApiModuleAConfiguration.class;
    }
}

现在,您的 ApiModuleAConfiguration,ApiModuleBConfiguration... 配置类将位于每个 API 模块项目模块 A-API项目模块 B-API 上。

它们可以是RepositoryRestMvcConfiguration,也可以是从中扩展,也可以是导入Spring Data REST配置的任何其他配置类。

最后但并非最不重要的一点是,我在主模块项目服务器中创建了不同的 gradle 脚本,这些脚本要根据传递给 gradle 的属性加载以模拟 Maven 配置文件。每个脚本都将需要包含的 api 模块声明为依赖项。它看起来像这样:

- project-server
    /profiles/
        profile-X.gradle
        profile-Y.gradle
        profile-Z.gradle

例如,配置文件 X 启用 API 模块 A 和 B:

dependencies {
    compile project(':project-module-a-api')
    compile project(':project-module-b-api')
}
processResources {
    from 'src/main/resources/profiles/profile-X'
    include 'profile-x.properties'
    into 'build/resources/main'
}

其他配置文件可以启用不同的 API 模块。

配置文件以这种方式从project-server.gradle加载:

loadProfile()
processResources {
    include '**/*'
    exclude 'profiles'
}
dependencies {
        compile (project(':project-api-autoconfigure')) {
            exclude module: 'project-module-a-api'
            exclude module: 'project-module-b-api'
            ...
        }
        ...
    }
...
def loadProfile() {
    def profile = hasProperty('profile') ? "${profile}" : "dev"
    println "Profile: " + profile
    apply from: "profiles/" + profile + ".gradle"
}

这或多或少都是。我希望它对你有所帮助。

干杯。

最新更新