So here is half the answer (or maybe the full answer, depending on how important it is to solve @ApplicationPath ).
To understand the solution below, you must first learn a little about the insides of Jersey. When we download our application, Jersey builds a model of all resources. All information for the resource is encapsulated in this model. Jersey uses this model to process requests, instead of trying to process resources for each request, store all the information about the resource in the model faster and process the model.
With this architecture, Jersey also allows you to create resources programmatically , using the same APIs as internally to hold model properties. Besides creating resource models, we can also modify existing models using ModelProcessor c.
In ModelProcessor we can introduce a Spring PropertyResolver , and then programmatically allow placeholders and replace the old resource model path with a resolved one. for instance
@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);
Regarding the resource model APIs
This is a Resource
@Path("resource") public class SomeResource { @GET public String get() {} }
His resource methods that are not annotated with @Path are ResourceMethod s
This is a child of the Resource above Resource , as it is annotated with @Path .
@GET @Path("child-resource") public String get() {}
This information should give you some insight into how this implementation works.
The following is a complete test using the Jersey Test Model . The following classpath properties file is used.
app.properties
resource=resource sub.resource=sub-resource sub.resource.locator=sub-resource-locator
You can run the following, like any other JUnit test.
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; 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);
And now, when I think about it, I see a way to use the @ApplicationPath solution, but it includes creating a Jersey servlet container programmatically in Spring WebAppInitializer . Honestly, I think it will be more trouble than it's worth it. I would just pull it in and leave @ApplicationPath as a static string.
UDPATE
If you are using Spring boot then the application path is definitely configured through the spring.jersey.applicationPath property. The Spring Jersey boot path is pretty much the idea that I had in mind in the previous paragraph, where you yourself create the Jersey servlet container and set up the servlet mapping. This is how it is configured using Spring Boot.