복붙노트

[SPRING] Jersey @Path 및 @ApplicationPath와 함께 Spring 속성 자리 표시 자 사용

SPRING

Jersey @Path 및 @ApplicationPath와 함께 Spring 속성 자리 표시 자 사용

내 프로젝트에서 저지와 봄을 사용합니다. 'jersey-spring3'은 이들을 통합하는 데 사용됩니다. 내 리소스 클래스를보다 유연하게 만들고 @Path 주석 내부의 속성을 사용하고 싶습니다.

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

그러나 Spring은 some.property를 Jersey의 주석 @Path 및 @ApplicationPath에 삽입 할 수 없습니다.

저지 자원의 @Path 값 내부에서 속성 파일을 사용하여 구성 가능한 값을 가질 수있는 방법이 있습니까?

(Jersey를 Spring MVC로 대체하는 것이 더 쉬울 것이라는 것을 알고 있지만, 불행히도이 선택 사항이 없습니다.)

해결법

  1. ==============================

    1.여기 절반의 대답이 있습니다 (또는 @ApplicationPath를 해결하는 것이 얼마나 중요한지에 따라 답변이 충분할 수도 있습니다).

    여기 절반의 대답이 있습니다 (또는 @ApplicationPath를 해결하는 것이 얼마나 중요한지에 따라 답변이 충분할 수도 있습니다).

    아래의 솔루션을 이해하려면 Jersey의 내부에 대해 약간 이해해야합니다. 애플리케이션을로드 할 때 Jersey는 모든 리소스 모델을 작성합니다. 리소스에 대한 모든 정보는이 모델에 캡슐화됩니다. Jersey는 각 요청에서 자원을 시도하고 처리하는 대신 요청 처리에이 모델을 사용하므로 모델에있는 자원에 대한 모든 정보를 유지하고 모델을 처리하는 것이 빠릅니다.

    이 아키텍처를 사용하여 Jersey는 모델 속성을 유지하기 위해 내부적으로 사용하는 것과 동일한 API를 사용하여 프로그래밍 방식으로 리소스를 빌드 할 수 있습니다. 자원 모델을 구축하는 것 외에도 ModelProcessors를 사용하여 기존 모델을 수정할 수도 있습니다.

    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에 관한 한

    이 정보는 위의 구현이 어떻게 작동하는지 몇 가지 아이디어를 제공합니다.

    아래는 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 서블릿 컨테이너를 만드는 것이 포함됩니다. Spring WebAppInitializer에서 프로그래밍 방식으로. 솔직히, 나는 그것이 가치가있는 것보다 더 어려울 것이라고 생각합니다. 그냥 빨아, @ApplicationPath 정적 문자열로 둡니다.

    스프링 부트를 사용하는 경우 spring.jersey.applicationPath 속성을 통해 응용 프로그램 경로를 확실히 구성 할 수 있습니다. Spring 부트가 Jersey를로드하는 방법은 Jersey 서블릿 컨테이너를 직접 만들고 서블릿 매핑을 설정하는 위 단락에서 생각한 아이디어입니다. Spring Boot로 설정하는 방법입니다.

  2. from https://stackoverflow.com/questions/34943650/using-spring-property-placeholders-with-jersey-path-and-applicationpath by cc-by-sa and MIT license