问题描述
我有一个使用Spring Boot生成可执行的JAR的项目,该JAR公开了带有Spring Data REST的REST API.它还与Spring Security OAuth集成在一起.很好我的问题如下,
I have a project that uses Spring Boot to generate an executable JAR that exposes a REST API with Spring Data REST. It is also integrated with Spring Security OAuth. That works fine. My problem is the following,
我想为REST API提供不同的模块,仅当具有JPA存储库的对应JAR位于类路径(已定义为依赖项)中时,才启用它.
I want to have different modules for the REST API that I want to enable only if the correspondent JAR with the JPA repositories is in the classpath (it has been defined as a dependency).
问题是我希望他们彼此独立.我希望能够在具有不同映射的不同调度程序servlet下为它们提供服务,以便为每个资源指定不同的baseUri,并为资源发现使用不同的根URL.
The thing is I want them to be independent from each other. I want to be able to serve them under different dispatcher servlets with different mappings so I can specify different baseUri for each one and have different root URLs for the resources discovery.
我将尝试使其更加清晰:
I´m gonna try to make it more clear:
-
API的模块A:
Module A of API:
- 一个JAR,其中包含资源X和Y的XRespository和YRespository.
- 调度程序servlet A.
- Servlet映射:/api/moduleA/
- Spring Data REST的基本URI:/api/moduleA/
- 如果我检查URL/api/moduleA/,我应该发现资源X和Y.
API模块B:
- 一个JAR,其中包含资源P和Q的PR存储库和QR存储库.
- 调度程序servlet B.
- Servlet映射:/api/moduleB/
- Spring Data REST的基本URI:/api/moduleB/
- 如果我检查URL/api/moduleB/,我应该发现资源P和Q.
更多模块...
除此之外,我可以拥有另一个调度程序servlet,在其中我将/oauth/*端点与其他自定义控制器一起保存,并且安全配置必须对所有(/*)正常工作
Apart from that I can have another dispatcher servlet where I hold /oauth/* endpoints along with other custom controllers, and the security configuration has to work properly for all (/*)
我知道我可以通过ServletRegistrationBean定义更多调度程序servlet,但是我不知道如何将它们附加到每个不同的spring数据其余配置.
I know I can define more dispatcher servlets through ServletRegistrationBean but I don´t know how to attach to each one different spring data rest configurations.
我还尝试通过在每个子上下文中使用定义每个调度程序servlet的配置,每个RepositoryRestMvcConfiguration以及每个@EnableJpaRepositories注释定义不同的要扫描的包,通过SpringApplicationBuilder在分层应用程序上下文中执行此操作.无论如何,我什至无法加载上下文,因为它们不是作为WebApplicationContext创建的,因此失败了,因为没有可用的ServletContext.
I´ve also been trying to do this with hierarchical application contexts with SpringApplicationBuilder by having in each child context the configuration that defines each dispatcher servlet, each RepositoryRestMvcConfiguration and having each @EnableJpaRepositories annotation defining different packages to scan. Anyway I can´t not even load the context as they are not created as WebApplicationContext thus failing because there is no ServletContext available.
有什么帮助/建议吗?预先感谢.
Any help/suggestion? Thanks in advance.
推荐答案
我前一阵子找到了解决方案,但忘了在这里分享,所以感谢Jan提醒我.
I found the solution a while ago but I forgot to share it here, so thanks Jan for reminding me that.
我通过在具有不同配置的新Web应用程序上下文( RepositoryRestMvcConfiguration )和一个公共父对象(即Spring Boot应用程序的根应用程序上下文)中创建和注册了多个调度程序servlet来解决了该问题.为了根据类路径中包含的不同jar自动启用API模块,我模拟了Spring Boot或多或少的功能.
I solved it by creating and registering several dispatchers servlets with new web application contexts with different configurations (RepositoryRestMvcConfiguration) and a common parent which is the root application context of the Spring Boot application. To enable the API modules automatically depending on the different jars included on the classpath I emulated what Spring Boot does more or less.
该项目分为多个gradle模块.像这样:
The project is divided in several gradle modules. Something like this:
- 项目服务器
- project-api-autoconfigure
- project-module-a-api
- project-module-b-api
- ...
- project-module-n-api
模块 project-server 是主要模块.它声明了对 project-api-autoconfigure 的依赖,同时还排除了 project-api-autoconfigure 对 project-module-的传递依赖. -api模块.
The module project-server is the main one. It declares a dependency on project-api-autoconfigure, and at the same time it excludes the transitive dependencies of project-api-autoconfigure on project-module-?-api module(s).
在 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 上看起来像这样:
project-api-autoconfigure depends on all the API modules, so the dependencies will look like this on 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模块的配置类每个API模块jar.
project-api-autoconfigure is where I create the dispatcher servlet beans with their own web application context for every API module, but this configurations are conditional on the configuration classes of every API module which live inside each API module jar.
我创建了一个抽象类,每个自动配置类都从该类继承:
I created and abstract class from which every autoconfiguration class inherit:
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的自动配置类如下所示:
So now, the autoconfiguration class for module A will look something like this:
@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模块 project-module-a-api 上, project-module-b-api ...
And now, your ApiModuleAConfiguration, ApiModuleBConfiguration... configuration classes will be on each api module project-module-a-api, project-module-b-api...
它们可以是 RepositoryRestMvcConfiguration 或可以从其扩展,也可以是导入Spring Data REST配置的任何其他配置类.
They can be RepositoryRestMvcConfiguration or they can extend from it or they can be any other configuration class that imports the Spring Data REST configuration.
最后但并非最不重要的一点是,我基于要传递给gradle的属性来模拟Maven配置文件的主模块 project-server 中创建了不同的gradle脚本,以供加载.每个脚本都将需要包含的api模块声明为依赖项.看起来像这样:
And last but not least, I created different gradle scripts inside the main module project-server to be loaded based on a property passed to gradle to emulate Maven profiles. Each script declares as dependencies the api modules that need to be included. It looks something like this:
- project-server
/profiles/
profile-X.gradle
profile-Y.gradle
profile-Z.gradle
,例如, profile-X 启用API模块A和B:
and for example, profile-X enables API modules A and 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模块.
Other profiles could enable different API modules.
个人资料是通过 project-server.gradle 加载的:
Profiles are loaded this way from the 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"
}
或多或少.希望对您有帮助.
And that's all more or less. I hope it helps you Jan.
干杯.
这篇关于Spring Boot(JAR)具有多个分派器servlet,用于带有Spring Data REST的不同REST API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!