gpt4 book ai didi

java - 将 Spring 属性占位符与 Jersey @Path 和 @ApplicationPath 一起使用

转载 作者:搜寻专家 更新时间:2023-10-31 20:00:51 26 4
gpt4 key购买 nike

我在项目中使用 Jersey 和 Spring。 'jersey-spring3' 用于它们之间的集成。我想让我的资源类更加灵活,并在 @Path 注释中使用属性,例如:

@Path("${some.property}/abc/def")

但是Spring不能给Jersey的注解@Path@ApplicationPath注入(inject)some.property

有没有什么方法可以在 Jersey 资源的 @Path 值中设置一些可配置的(使用属性文件)值?

(我意识到用 Spring MVC 替换 Jersey 会更容易,但不幸的是,就我而言,我没有这个选择。)

最佳答案

所以这是答案的一半(或者可能是完整答案,具体取决于解析 @ApplicationPath 对您的重要性)。

要理解下面的解决方案,您应该首先了解一些 Jersey 的内部结构。当我们加载应用程序时,Jersey 会构建所有资源的模型。资源的所有信息都封装在这个模型中。 Jersey 使用这个模型来处理请求,而不是尝试在每个请求上处理资源,将有关资源的所有信息保存在模型中并处理模型会更快。

有了这个架构,Jersey 还允许我们 build resources programmatically ,使用它在内部使用的相同 API 来保存模型属性。除了构建资源模型,我们还可以modify existing models , 使用 ModelProcessor

ModelProcessor 中,我们可以注入(inject) 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而言

  • 这是一个资源

    @Path("resource")
    public class SomeResource {
    @GET
    public String get() {}
    }

    @Path注解的资源方法是ResourceMethod

  • 这是上述 Resourcechild 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 中。老实说,我认为这会比它的值(value)更麻烦。我只是接受它,并将 @ApplicationPath 保留为静态字符串。


更新

如果您使用的是 Spring boot,那么应用程序路径绝对是可配置的,通过 spring.jersey.applicationPath 属性。 Spring boot 加载 Jersey 的方式几乎就是我在上一段中想到的想法,您自己在其中创建 Jersey servlet 容器,并设置 servlet 映射。这就是使用 Spring Boot 进行配置的方式。

关于java - 将 Spring 属性占位符与 Jersey @Path 和 @ApplicationPath 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34943650/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com