带有多个调度程序servlet的Spring Boot(JAR),用于使用Spring Data REST的不同REST API

Dan*_*gal 6 java spring spring-data-jpa spring-data-rest spring-boot

我有一个使用Spring Boot生成可执行JAR的项目,该JAR使用Spring Data REST公开REST API.它还与Spring Security OAuth集成.这很好.我的问题如下,

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

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

我会试着让它更清楚:

  • API模块A:

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

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

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

我知道我可以通过ServletRegistrationBean定义更多的调度程序servlet,但我不知道如何附加到每个不同的弹簧数据休息配置.

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

任何帮助/建议?提前致谢.

Dan*_*gal 7

我刚才找到了解决方案,但我忘了在这里分享,所以感谢Jan提醒我.

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

该项目分为几个gradle模块.像这样的东西:

  • 项目服务器
  • 项目-API自动配置
  • 项目模块-A-API
  • 项目模块-B-API
  • ...
  • 项目模块正API

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

project-server.gradle中:

dependencies {
    compile (project(':project-api-autoconfigure')) {
        exclude module: 'project-module-a-api'
        exclude module: 'project-module-b-api'
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

dependencies {
    compile project(':project-module-a-api')
    compile project(':project-module-b-api')
    ...
}
Run Code Online (Sandbox Code Playgroud)

project-api-autoconfigure是我为每个API模块创建带有自己的Web应用程序上下文的调度程序servlet bean的地方,但是这些配置是以每个API模块jar中的每个API模块的配置类为条件的.

我创建并抽象了每个autoconfiguration类继承的类:

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();

}
Run Code Online (Sandbox Code Playgroud)

那么现在,模块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;
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,你的ApiModuleAConfiguration,ApiModuleBConfiguration ......配置类将在每个api模块项目 - module-a-api,project-module-b-api上 ...

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

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

- project-server
    /profiles/
        profile-X.gradle
        profile-Y.gradle
        profile-Z.gradle
Run Code Online (Sandbox Code Playgroud)

例如,profile-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'
}
Run Code Online (Sandbox Code Playgroud)

其他配置文件可以启用不同的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"
}
Run Code Online (Sandbox Code Playgroud)

这或多或少都是.我希望它可以帮助你1月.

干杯.