我在项目中使用Jersey和Spring。 '球衣-spring3'用于它们之间的集成。我想使我的资源类更灵活,并使用@Path("${some.property}/abc/def")
注释中的属性,如:
some.property
但是Spring无法向泽西岛的注释@Path
和@ApplicationPath
注入@Path
。
是否有任何方法可以在Jersey的资源{{1}}值内部配置一些(使用属性文件)值?
(我意识到用Spring MVC取代泽西会更容易,但不幸的是我不能做出这个选择。)
答案 0 :(得分:5)
所以这里只有答案的一半(或者可能是一个完整的答案,取决于解析@ApplicationPath
对你的重要程度)。
要理解以下解决方案,首先应该了解一下泽西的内部情况。当我们加载我们的应用程序时,Jersey构建了所有资源的模型。资源的所有信息都封装在此模型中。 Jersey使用此模型处理请求,而不是尝试在每个请求上处理资源,更快地将有关资源的所有信息保存在模型中,并处理模型。
使用这种架构,Jersey还允许我们build resources programmatically,使用它在内部使用的相同API来保存模型属性。除了构建资源模型之外,我们还可以使用modify existing models来ModelProcessor
。
在ModelProcessor
中,我们可以注入Spring的PropertyResolver
,然后以编程方式解析占位符,并用已解析的路径替换旧的资源模型路径。例如
@Autowired
private PropertyResolver propertyResolver;
private ResourceModel processResourceModel(ResourceModel resourceModel) {
ResourceModel.Builder newResourceModelBuilder = new ResourceModel.Builder(false);
for (final Resource resource : resourceModel.getResources()) {
final Resource.Builder resourceBuilder = Resource.builder(resource);
String resolvedResourcePath = processPropertyPlaceholder(resource);
resourceBuilder.path(resolvedResourcePath);
// handle child resources
for (Resource childResource : resource.getChildResources()) {
String resolvedChildPath = processPropertyPlaceholder(childResource);
final Resource.Builder childResourceBuilder = Resource.builder(childResource);
childResourceBuilder.path(resolvedChildPath);
resourceBuilder.addChildResource(childResourceBuilder.build());
}
newResourceModelBuilder.addResource(resourceBuilder.build());
}
return newResourceModelBuilder.build();
}
private String processPropertyPlaceholder(Resource resource) {
String ogPath = resource.getPath();
return propertyResolver.resolvePlaceholders(ogPath);
}
就资源模型API而言
这是Resource
@Path("resource")
public class SomeResource {
@GET
public String get() {}
}
注释@Path
的资源方法是ResourceMethod
s
这是上述Resource
的孩子 Resource
,因为它使用@Path
进行了注释。
@GET
@Path("child-resource")
public String get() {}
此信息可以让您了解上述实施的工作原理。
以下是使用Jersey Test Framework的完整测试。使用以下类路径属性文件
<强> app.properties
强>
resource=resource
sub.resource=sub-resource
sub.resource.locator=sub-resource-locator
您可以像任何其他JUnit测试一样运行以下命令。
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.ModelProcessor;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceModel;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.PropertyResolver;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
* Stack Overflow http://stackoverflow.com/q/34943650/2587435
*
* Run it like any other JUnit test. Required dependencies are as follows:
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>2.22.1</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>org.glassfish.jersey.ext</groupId>
* <artifactId>jersey-spring3</artifactId>
* <version>2.22.1</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>commons-logging</groupId>
* <artifactId>commons-logging</artifactId>
* <version>1.1</version>
* <scope>test</scope>
* </dependency>
*
* @author Paul Samsotha
*/
public class SpringPathResolverTest extends JerseyTest {
@Path("${resource}")
public static class TestResource {
@GET
public String get() {
return "Resource Success!";
}
@GET
@Path("${sub.resource}")
public String getSubMethod() {
return "Sub-Resource Success!";
}
@Path("${sub.resource.locator}")
public SubResourceLocator getSubResourceLocator() {
return new SubResourceLocator();
}
public static class SubResourceLocator {
@GET
public String get() {
return "Sub-Resource-Locator Success!";
}
}
}
@Configuration
@PropertySource("classpath:/app.properties")
public static class SpringConfig {
}
public static class PropertyPlaceholderPathResolvingModelProcessor
implements ModelProcessor {
@Autowired
private PropertyResolver propertyResolver;
@Override
public ResourceModel processResourceModel(ResourceModel resourceModel,
javax.ws.rs.core.Configuration configuration) {
return processResourceModel(resourceModel);
}
@Override
public ResourceModel processSubResource(ResourceModel subResourceModel,
javax.ws.rs.core.Configuration configuration) {
return subResourceModel;
}
private ResourceModel processResourceModel(ResourceModel resourceModel) {
ResourceModel.Builder newResourceModelBuilder = new ResourceModel.Builder(false);
for (final Resource resource : resourceModel.getResources()) {
final Resource.Builder resourceBuilder = Resource.builder(resource);
String resolvedResourcePath = processPropertyPlaceholder(resource);
resourceBuilder.path(resolvedResourcePath);
// handle child resources
for (Resource childResource : resource.getChildResources()) {
String resolvedChildPath = processPropertyPlaceholder(childResource);
final Resource.Builder childResourceBuilder = Resource.builder(childResource);
childResourceBuilder.path(resolvedChildPath);
resourceBuilder.addChildResource(childResourceBuilder.build());
}
newResourceModelBuilder.addResource(resourceBuilder.build());
}
return newResourceModelBuilder.build();
}
private String processPropertyPlaceholder(Resource resource) {
String ogPath = resource.getPath();
return propertyResolver.resolvePlaceholders(ogPath);
}
}
@Override
public ResourceConfig configure() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
return new ResourceConfig(TestResource.class)
.property("contextConfig", ctx)
.register(PropertyPlaceholderPathResolvingModelProcessor.class)
.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
@Test
public void pathPlaceholderShouldBeResolved() {
Response response = target("resource").request().get();
assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is(equalTo("Resource Success!")));
response.close();
response = target("resource/sub-resource").request().get();
assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is(equalTo("Sub-Resource Success!")));
response.close();
response = target("resource/sub-resource-locator").request().get();
assertThat(response.getStatus(), is(200));
assertThat(response.readEntity(String.class), is(equalTo("Sub-Resource-Locator Success!")));
response.close();
}
}
现在我考虑一下,我可以看到使用解析@ApplicationPath
的方法,但它涉及创建Jersey servlet容器
以编程方式在Spring WebAppInitializer
中。老实说,我认为这比它的价值更麻烦。我只是把它搞砸了,把@ApplicationPath
留作静态字符串。
如果您使用的是Spring引导,那么应用程序路径绝对可以通过spring.jersey.applicationPath
属性进行配置。 Spring引导加载Jersey的方式几乎就是我在上面的段落中想到的,你自己创建了Jersey servlet容器,并设置了servlet映射。这是Spring Boot可配置的方式。