From 5776d6a8d71feb2cdfd706262f8eb3c8e0a766a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 14 Oct 2015 14:04:47 -0500 Subject: [PATCH 1/2] Add support of Jackson in Jersey If Jackson is configured for the project, the `ObjectMapper` is now reused and added in the Jersey's context. Closes gh-4131 --- spring-boot-autoconfigure/pom.xml | 5 + .../jersey/JerseyAutoConfiguration.java | 52 +++++++ .../jersey/ResourceConfigCustomizer.java | 34 +++++ ...rationCustomObjectMapperProviderTests.java | 135 ++++++++++++++++++ ...onfigurationObjectMapperProviderTests.java | 135 ++++++++++++++++++ 5 files changed, 361 insertions(+) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 1493a6a1a60..4500ac787c9 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -130,6 +130,11 @@ jersey-spring3 true + + org.glassfish.jersey.media + jersey-media-json-jackson + true + commons-dbcp commons-dbcp diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java index 3c30ecdb71d..163b6691462 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java @@ -26,15 +26,21 @@ import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.ws.rs.ApplicationPath; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Provider; + +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.glassfish.jersey.CommonProperties; +import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.glassfish.jersey.servlet.ServletProperties; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -43,6 +49,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.embedded.RegistrationBean; @@ -73,6 +80,7 @@ import org.springframework.web.filter.RequestContextFilter; @ConditionalOnWebApplication @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureBefore(DispatcherServletAutoConfiguration.class) +@AutoConfigureAfter(JacksonAutoConfiguration.class) @EnableConfigurationProperties(JerseyProperties.class) public class JerseyAutoConfiguration implements ServletContextAware { @@ -84,10 +92,24 @@ public class JerseyAutoConfiguration implements ServletContextAware { @Autowired private ResourceConfig config; + @Autowired(required = false) + private ResourceConfigCustomizer customizer; + private String path; @PostConstruct public void path() { + resolveApplicationPath(); + applyCustomConfig(); + } + + private void applyCustomConfig() { + if (this.customizer != null) { + this.customizer.customize(this.config); + } + } + + private void resolveApplicationPath() { if (StringUtils.hasLength(this.jersey.getApplicationPath())) { this.path = parseApplicationPath(this.jersey.getApplicationPath()); } @@ -193,6 +215,36 @@ public class JerseyAutoConfiguration implements ServletContextAware { // will try and register a ContextLoaderListener which we don't need servletContext.setInitParameter("contextConfigLocation", ""); } + } + + @ConditionalOnClass(JacksonFeature.class) + @Configuration + static class ObjectMapperResourceConfigCustomizer { + + @Bean + public ResourceConfigCustomizer resourceConfigCustomizer() { + return new ResourceConfigCustomizer() { + @Override + public void customize(ResourceConfig config) { + config.register(JacksonFeature.class); + config.register(ObjectMapperContextResolver.class); + } + }; + } + + @Provider + static class ObjectMapperContextResolver + implements ContextResolver { + + @Autowired + private ObjectMapper objectMapper; + + @Override + public ObjectMapper getContext(Class type) { + return this.objectMapper; + } + + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java new file mode 100644 index 00000000000..030d8912d8b --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jersey; + +import org.glassfish.jersey.server.ResourceConfig; + +import org.springframework.context.annotation.Configuration; + +/** + * Callback for customizing the Jersey {@link Configuration}. + * + * @author Eddú Meléndez + * @since 1.4.0 + * @see JerseyAutoConfiguration + */ +public interface ResourceConfigCustomizer { + + void customize(ResourceConfig config); + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java new file mode 100644 index 00000000000..b581f589f52 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java @@ -0,0 +1,135 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jersey; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfigurationObjectMapperProviderTests.Application; +import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; +import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.TestRestTemplate; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.client.RestTemplate; + +import static org.junit.Assert.assertEquals; + +/** + * @author Eddú Meléndez + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(Application.class) +@IntegrationTest({ "server.port=0", "spring.jackson.serialization-inclusion=non_null" }) +@WebAppConfiguration +public class JerseyAutoConfigurationCustomObjectMapperProviderTests { + + @Value("${local.server.port}") + private int port; + + private RestTemplate restTemplate = new TestRestTemplate(); + + @Test + public void contextLoads() { + ResponseEntity response = this.restTemplate.getForEntity( + "http://localhost:" + this.port + "/rest/message", String.class); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("{\"subject\":\"Jersey\"}", response.getBody()); + } + + @MinimalWebConfiguration + @ApplicationPath("/rest") + @Path("/message") + public static class Application extends ResourceConfig { + + @GET + public Message message() { + return new Message("Jersey", null); + } + + public Application() { + register(Application.class); + } + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + } + + public static class Message { + + private String subject; + + private String body; + + public Message() { + + } + + public Message(String subject, String body) { + this.subject = subject; + this.body = body; + } + + public String getSubject() { + return this.subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return this.body; + } + + public void setBody(String body) { + this.body = body; + } + } + + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Import({ EmbeddedServletContainerAutoConfiguration.class, + JacksonAutoConfiguration.class, ServerPropertiesAutoConfiguration.class, + JerseyAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) + protected @interface MinimalWebConfiguration { + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java new file mode 100644 index 00000000000..44c49fe378f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java @@ -0,0 +1,135 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jersey; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfigurationObjectMapperProviderTests.Application; +import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; +import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.TestRestTemplate; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.client.RestTemplate; + +import static org.junit.Assert.assertEquals; + +/** + * @author Eddú Meléndez + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(Application.class) +@IntegrationTest("server.port=0") +@WebAppConfiguration +public class JerseyAutoConfigurationObjectMapperProviderTests { + + @Value("${local.server.port}") + private int port; + + private RestTemplate restTemplate = new TestRestTemplate(); + + @Test + public void contextLoads() { + ResponseEntity response = this.restTemplate.getForEntity( + "http://localhost:" + this.port + "/rest/message", String.class); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("{\"subject\":\"Jersey\",\"body\":null}", response.getBody()); + } + + @MinimalWebConfiguration + @ApplicationPath("/rest") + @Path("/message") + public static class Application extends ResourceConfig { + + @GET + public Message message() { + return new Message("Jersey", null); + } + + public Application() { + register(Application.class); + } + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + } + + public static class Message { + + private String subject; + + private String body; + + public Message() { + + } + + public Message(String subject, String body) { + this.subject = subject; + this.body = body; + } + + public String getSubject() { + return this.subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return this.body; + } + + public void setBody(String body) { + this.body = body; + } + } + + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Import({ EmbeddedServletContainerAutoConfiguration.class, + JacksonAutoConfiguration.class, ServerPropertiesAutoConfiguration.class, + JerseyAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) + protected @interface MinimalWebConfiguration { + } + +} From a27176807f4b45bc768e019d9a0dab1ac8ec3dec Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 8 Feb 2016 15:34:22 +0100 Subject: [PATCH 2/2] Polish contribution Closes gh-4188 --- .../jersey/JerseyAutoConfiguration.java | 25 ++++++++++++------- .../jersey/ResourceConfigCustomizer.java | 10 +++++--- ...rationCustomObjectMapperProviderTests.java | 7 +++--- ...onfigurationObjectMapperProviderTests.java | 6 ++--- .../main/asciidoc/spring-boot-features.adoc | 3 +++ 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java index 163b6691462..252ea2c2e84 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jersey; import java.util.Arrays; import java.util.EnumSet; +import java.util.List; import java.util.Map.Entry; import javax.annotation.PostConstruct; @@ -48,6 +49,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; @@ -58,6 +60,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.util.ClassUtils; @@ -93,20 +96,14 @@ public class JerseyAutoConfiguration implements ServletContextAware { private ResourceConfig config; @Autowired(required = false) - private ResourceConfigCustomizer customizer; + private List customizers; private String path; @PostConstruct public void path() { resolveApplicationPath(); - applyCustomConfig(); - } - - private void applyCustomConfig() { - if (this.customizer != null) { - this.customizer.customize(this.config); - } + customize(); } private void resolveApplicationPath() { @@ -119,6 +116,15 @@ public class JerseyAutoConfiguration implements ServletContextAware { } } + private void customize() { + if (this.customizers != null) { + AnnotationAwareOrderComparator.sort(this.customizers); + for (ResourceConfigCustomizer customizer : this.customizers) { + customizer.customize(this.config); + } + } + } + @Bean @ConditionalOnMissingBean public FilterRegistrationBean requestContextFilter() { @@ -218,8 +224,9 @@ public class JerseyAutoConfiguration implements ServletContextAware { } @ConditionalOnClass(JacksonFeature.class) + @ConditionalOnSingleCandidate(ObjectMapper.class) @Configuration - static class ObjectMapperResourceConfigCustomizer { + static class JacksonResourceConfigCustomizer { @Bean public ResourceConfigCustomizer resourceConfigCustomizer() { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java index 030d8912d8b..0bb08cc030d 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java @@ -18,17 +18,19 @@ package org.springframework.boot.autoconfigure.jersey; import org.glassfish.jersey.server.ResourceConfig; -import org.springframework.context.annotation.Configuration; - /** - * Callback for customizing the Jersey {@link Configuration}. + * Callback interface that can be implemented by beans wishing to customize Jersey's + * {@link ResourceConfig} before it is used. * * @author Eddú Meléndez * @since 1.4.0 - * @see JerseyAutoConfiguration */ public interface ResourceConfigCustomizer { + /** + * Customize the resource config. + * @param config the {@link ResourceConfig} to customize + */ void customize(ResourceConfig config); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java index b581f589f52..ec2b55812a8 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java @@ -47,7 +47,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.client.RestTemplate; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Eddú Meléndez @@ -67,8 +68,8 @@ public class JerseyAutoConfigurationCustomObjectMapperProviderTests { public void contextLoads() { ResponseEntity response = this.restTemplate.getForEntity( "http://localhost:" + this.port + "/rest/message", String.class); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertEquals("{\"subject\":\"Jersey\"}", response.getBody()); + assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode()); + assertThat("{\"subject\":\"Jersey\"}").isEqualTo(response.getBody()); } @MinimalWebConfiguration diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java index 44c49fe378f..87234ee25a7 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java @@ -47,7 +47,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.client.RestTemplate; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Eddú Meléndez @@ -67,8 +67,8 @@ public class JerseyAutoConfigurationObjectMapperProviderTests { public void contextLoads() { ResponseEntity response = this.restTemplate.getForEntity( "http://localhost:" + this.port + "/rest/message", String.class); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertEquals("{\"subject\":\"Jersey\",\"body\":null}", response.getBody()); + assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode()); + assertThat("{\"subject\":\"Jersey\",\"body\":null}").isEqualTo(response.getBody()); } @MinimalWebConfiguration diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index ab4dac8ff70..57c77b3bc8b 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1754,6 +1754,9 @@ all the endpoints: } ---- +You can also register an arbitrary number of beans implementing `ResourceConfigCustomizer` +for more advanced customizations. + All the registered endpoints should be `@Components` with HTTP resource annotations (`@GET` etc.), e.g.