From ba552f1d2448c71b888f2cc5ee8c574a5d35d5d2 Mon Sep 17 00:00:00 2001 From: ioann Date: Thu, 4 Jan 2018 17:14:03 +0300 Subject: [PATCH] Allow GSON customization via properties or beans Update GSON support to allow customization with either properties or customize beans. See gh-11498 --- .../gson/GsonAutoConfiguration.java | 127 ++++++++- .../gson/GsonBuilderCustomizer.java | 37 +++ .../autoconfigure/gson/GsonProperties.java | 190 ++++++++++++++ .../gson/GsonAutoConfigurationTests.java | 245 ++++++++++++++++-- .../appendix-application-properties.adoc | 13 + 5 files changed, 587 insertions(+), 25 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonBuilderCustomizer.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonProperties.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java index fe667a3e598..ab51bf97993 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2014 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. @@ -16,28 +16,145 @@ package org.springframework.boot.autoconfigure.gson; +import java.util.List; + +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.LongSerializationPolicy; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.Ordered; /** * {@link EnableAutoConfiguration Auto-configuration} for Gson. * * @author David Liu + * @author Ivan Golovko * @since 1.2.0 */ @Configuration @ConditionalOnClass(Gson.class) public class GsonAutoConfiguration { - @Bean - @ConditionalOnMissingBean - public Gson gson() { - return new Gson(); + @Configuration + static class GsonConfiguration { + + @Bean + @Primary + @ConditionalOnMissingBean(Gson.class) + public Gson gson(GsonBuilder gsonBuilder) { + return gsonBuilder.create(); + } + } + + @Configuration + static class GsonBuilderConfiguration { + + @Bean + public GsonBuilder gsonBuilder(List customizers) { + final GsonBuilder gsonBuilder = new GsonBuilder(); + customizers.forEach(c -> c.customize(gsonBuilder)); + return gsonBuilder; + } + + } + + @Configuration + @EnableConfigurationProperties(GsonProperties.class) + static class GsonBuilderCustomizerConfiguration { + + @Bean + public StandardGsonBuilderCustomizer standardGsonBuilderCustomizer( + GsonProperties gsonProperties) { + return new StandardGsonBuilderCustomizer(gsonProperties); + } + + private static final class StandardGsonBuilderCustomizer + implements GsonBuilderCustomizer, Ordered { + + private final GsonProperties properties; + + StandardGsonBuilderCustomizer(GsonProperties properties) { + this.properties = properties; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(GsonBuilder gsonBuilder) { + + boolean generateNonExecutableJson = this.properties + .isGenerateNonExecutableJson(); + if (generateNonExecutableJson) { + gsonBuilder.generateNonExecutableJson(); + } + + boolean excludeFieldsWithoutExposeAnnotation = this.properties + .isExcludeFieldsWithoutExposeAnnotation(); + if (excludeFieldsWithoutExposeAnnotation) { + gsonBuilder.excludeFieldsWithoutExposeAnnotation(); + } + + boolean serializeNulls = this.properties.isSerializeNulls(); + if (serializeNulls) { + gsonBuilder.serializeNulls(); + } + + boolean enableComplexMapKeySerialization = this.properties + .isEnableComplexMapKeySerialization(); + if (enableComplexMapKeySerialization) { + gsonBuilder.enableComplexMapKeySerialization(); + } + + boolean disableInnerClassSerialization = this.properties + .isDisableInnerClassSerialization(); + if (disableInnerClassSerialization) { + gsonBuilder.disableInnerClassSerialization(); + } + + LongSerializationPolicy longSerializationPolicy = this.properties + .getLongSerializationPolicy(); + if (longSerializationPolicy != null) { + gsonBuilder.setLongSerializationPolicy(longSerializationPolicy); + } + + FieldNamingPolicy fieldNamingPolicy = this.properties + .getFieldNamingPolicy(); + if (fieldNamingPolicy != null) { + gsonBuilder.setFieldNamingPolicy(fieldNamingPolicy); + } + + boolean prettyPrinting = this.properties.isPrettyPrinting(); + if (prettyPrinting) { + gsonBuilder.setPrettyPrinting(); + } + + boolean isLenient = this.properties.isLenient(); + if (isLenient) { + gsonBuilder.setLenient(); + } + + boolean disableHtmlEscaping = this.properties.isDisableHtmlEscaping(); + if (disableHtmlEscaping) { + gsonBuilder.disableHtmlEscaping(); + } + + String dateFormat = this.properties.getDateFormat(); + if (dateFormat != null) { + gsonBuilder.setDateFormat(dateFormat); + } + } + } } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonBuilderCustomizer.java new file mode 100644 index 00000000000..cf9054f3e6c --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonBuilderCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2017 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.gson; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Callback interface that can be implemented by beans wishing to further customize the + * {@link Gson} via {@link GsonBuilder} retaining its default auto-configuration. + * + * @author Ivan Golovko + * @since 2.0.0 + */ +@FunctionalInterface +public interface GsonBuilderCustomizer { + + /** + * Customize the GsonBuilder. + * @param gsonBuilder the GsonBuilder to customize + */ + void customize(GsonBuilder gsonBuilder); +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonProperties.java new file mode 100644 index 00000000000..2ca8f96d703 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonProperties.java @@ -0,0 +1,190 @@ +/* + * Copyright 2012-2017 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.gson; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.LongSerializationPolicy; +import com.google.gson.annotations.Expose; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties to configure {@link Gson}. + * + * @author Ivan Golovko + * @since 2.0.0 + */ +@ConfigurationProperties(prefix = "spring.gson") +public class GsonProperties { + + /** + * Makes the output JSON non-executable in Javascript by prefixing the generated JSON + * with some special text. + */ + private boolean generateNonExecutableJson; + + /** + * Configures {@link Gson} to exclude all fields from consideration for serialization + * or deserialization that do not have the {@link Expose} annotation. + */ + private boolean excludeFieldsWithoutExposeAnnotation; + + /** + * Configure {@link Gson} to serialize null fields. + */ + private boolean serializeNulls; + + /** + * Enabling this feature will only change the serialized form if the map key is a + * complex type (i.e. non-primitive) in its serialized JSON form + */ + private boolean enableComplexMapKeySerialization; + + /** + * Configures {@link Gson} to exclude inner classes during serialization. + */ + private boolean disableInnerClassSerialization; + + /** + * Configures {@link Gson} to apply a specific serialization policy for Long and long + * objects. + */ + private LongSerializationPolicy longSerializationPolicy; + + /** + * Configures {@link Gson} to apply a specific naming policy to an object's field + * during serialization and deserialization. + */ + private FieldNamingPolicy fieldNamingPolicy; + + /** + * Configures {@link Gson} to output Json that fits in a page for pretty printing. + * This option only affects Json serialization. + */ + private boolean prettyPrinting; + + /** + * By default, {@link Gson} is strict and only accepts JSON as specified by RFC 4627. + * This option makes the parser liberal in what it accepts. + */ + private boolean lenient; + + /** + * By default, {@link Gson} escapes HTML characters such as < > etc. Use this option + * to configure Gson to pass-through HTML characters as is. + */ + private boolean disableHtmlEscaping; + + /** + * Configures {@link Gson} to serialize Date objects according to the pattern + * provided. + */ + private String dateFormat; + + public boolean isGenerateNonExecutableJson() { + return this.generateNonExecutableJson; + } + + public void setGenerateNonExecutableJson(boolean generateNonExecutableJson) { + this.generateNonExecutableJson = generateNonExecutableJson; + } + + public boolean isExcludeFieldsWithoutExposeAnnotation() { + return this.excludeFieldsWithoutExposeAnnotation; + } + + public void setExcludeFieldsWithoutExposeAnnotation( + boolean excludeFieldsWithoutExposeAnnotation) { + this.excludeFieldsWithoutExposeAnnotation = excludeFieldsWithoutExposeAnnotation; + } + + public boolean isSerializeNulls() { + return this.serializeNulls; + } + + public void setSerializeNulls(boolean serializeNulls) { + this.serializeNulls = serializeNulls; + } + + public boolean isEnableComplexMapKeySerialization() { + return this.enableComplexMapKeySerialization; + } + + public void setEnableComplexMapKeySerialization( + boolean enableComplexMapKeySerialization) { + this.enableComplexMapKeySerialization = enableComplexMapKeySerialization; + } + + public boolean isDisableInnerClassSerialization() { + return this.disableInnerClassSerialization; + } + + public void setDisableInnerClassSerialization( + boolean disableInnerClassSerialization) { + this.disableInnerClassSerialization = disableInnerClassSerialization; + } + + public LongSerializationPolicy getLongSerializationPolicy() { + return this.longSerializationPolicy; + } + + public void setLongSerializationPolicy( + LongSerializationPolicy longSerializationPolicy) { + this.longSerializationPolicy = longSerializationPolicy; + } + + public FieldNamingPolicy getFieldNamingPolicy() { + return this.fieldNamingPolicy; + } + + public void setFieldNamingPolicy(FieldNamingPolicy fieldNamingPolicy) { + this.fieldNamingPolicy = fieldNamingPolicy; + } + + public boolean isPrettyPrinting() { + return this.prettyPrinting; + } + + public void setPrettyPrinting(boolean prettyPrinting) { + this.prettyPrinting = prettyPrinting; + } + + public boolean isLenient() { + return this.lenient; + } + + public void setLenient(boolean lenient) { + this.lenient = lenient; + } + + public boolean isDisableHtmlEscaping() { + return this.disableHtmlEscaping; + } + + public void setDisableHtmlEscaping(boolean disableHtmlEscaping) { + this.disableHtmlEscaping = disableHtmlEscaping; + } + + public String getDateFormat() { + return this.dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java index fe20419b882..f077a0c73f3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -16,49 +16,254 @@ package org.springframework.boot.autoconfigure.gson; +import java.lang.reflect.Field; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import org.junit.After; -import org.junit.Before; +import com.google.gson.LongSerializationPolicy; +import org.joda.time.DateTime; import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; import static org.assertj.core.api.Assertions.assertThat; - /** * Tests for {@link GsonAutoConfiguration}. * * @author David Liu + * @author Ivan Golovko */ public class GsonAutoConfigurationTests { - AnnotationConfigApplicationContext context; + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GsonAutoConfiguration.class)); - @Before - public void setUp() { - this.context = new AnnotationConfigApplicationContext(); + @Test + public void gsonRegistration() { + this.contextRunner.run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())).isEqualTo("{\"data\":1}"); + }); } - @After - public void tearDown() { - if (this.context != null) { - this.context.close(); - } + @Test + public void generateNonExecutableJson() { + this.contextRunner + .withPropertyValues("spring.gson.generate-non-executable-json:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())) + .isNotEqualTo("{\"data\":1}"); + assertThat(gson.toJson(new DataObject())).endsWith("{\"data\":1}"); + }); } @Test - public void gsonRegistration() { - this.context.register(GsonAutoConfiguration.class); - this.context.refresh(); - Gson gson = this.context.getBean(Gson.class); - assertThat(gson.toJson(new DataObject())).isEqualTo("{\"data\":\"hello\"}"); + public void excludeFieldsWithoutExposeAnnotation() { + this.contextRunner + .withPropertyValues( + "spring.gson.exclude-fields-without-expose-annotation:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())).isEqualTo("{}"); + }); + } + + @Test + public void serializeNulls() { + this.contextRunner.withPropertyValues("spring.gson.serialize-nulls:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.serializeNulls()).isTrue(); + }); + } + + @Test + public void enableComplexMapKeySerialization() { + this.contextRunner + .withPropertyValues( + "spring.gson.enable-complex-map-key-serialization:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + Map original = new LinkedHashMap<>(); + original.put(new DataObject(), "a"); + assertThat(gson.toJson(original)).isEqualTo("[[{\"data\":1},\"a\"]]"); + }); + } + + @Test + public void notDisableInnerClassSerialization() { + this.contextRunner.run(context -> { + Gson gson = context.getBean(Gson.class); + WrapperObject wrapperObject = new WrapperObject(); + assertThat(gson.toJson(wrapperObject.new NestedObject())) + .isEqualTo("{\"data\":\"nested\"}"); + }); + } + + @Test + public void disableInnerClassSerialization() { + this.contextRunner + .withPropertyValues("spring.gson.disable-inner-class-serialization:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + WrapperObject wrapperObject = new WrapperObject(); + assertThat(gson.toJson(wrapperObject.new NestedObject())) + .isEqualTo("null"); + }); + } + + @Test + public void withLongSerializationPolicy() { + this.contextRunner.withPropertyValues( + "spring.gson.long-serialization-policy:" + LongSerializationPolicy.STRING) + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())) + .isEqualTo("{\"data\":\"1\"}"); + }); + } + + @Test + public void withFieldNamingPolicy() { + FieldNamingPolicy fieldNamingPolicy = FieldNamingPolicy.UPPER_CAMEL_CASE; + this.contextRunner + .withPropertyValues( + "spring.gson.field-naming-policy:" + fieldNamingPolicy) + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.fieldNamingStrategy()).isEqualTo(fieldNamingPolicy); + }); + } + + @Test + public void additionalGsonBuilderCustomization() { + this.contextRunner.withUserConfiguration(GsonBuilderCustomConfig.class) + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())).isEqualTo("{}"); + }); + } + + @Test + public void withPrettyPrinting() { + this.contextRunner.withPropertyValues("spring.gson.pretty-printing:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())) + .isEqualTo("{\n \"data\": 1\n}"); + }); + } + + @Test + public void withoutLenient() throws Exception { + this.contextRunner.run(context -> { + Gson gson = context.getBean(Gson.class); + /* + * It seems, that lenient setting not work in version 2.8.2 We get access to + * it via reflection + */ + Field lenientField = gson.getClass().getDeclaredField("lenient"); + lenientField.setAccessible(true); + boolean lenient = lenientField.getBoolean(gson); + + assertThat(lenient).isFalse(); + }); + } + + @Test + public void withLenient() throws Exception { + this.contextRunner.withPropertyValues("spring.gson.lenient:true").run(context -> { + Gson gson = context.getBean(Gson.class); + + /* + * It seems, that lenient setting not work in version 2.8.0 of gson We get + * access to it via reflection + */ + Field lenientField = gson.getClass().getDeclaredField("lenient"); + lenientField.setAccessible(true); + boolean lenient = lenientField.getBoolean(gson); + + assertThat(lenient).isTrue(); + }); + } + + @Test + public void withHtmlEscaping() { + this.contextRunner.run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.htmlSafe()).isTrue(); + }); + } + + @Test + public void withoutHtmlEscaping() { + this.contextRunner.withPropertyValues("spring.gson.disable-html-escaping:true") + .run(context -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.htmlSafe()).isFalse(); + }); + + } + + @Test + public void customDateFormat() { + this.contextRunner.withPropertyValues("spring.gson.date-format:H") + .run(context -> { + Gson gson = context.getBean(Gson.class); + DateTime dateTime = new DateTime(1988, 6, 25, 20, 30); + Date date = dateTime.toDate(); + assertThat(gson.toJson(date)).isEqualTo("\"20\""); + }); + } + + protected static class GsonBuilderCustomConfig { + + @Bean + public GsonBuilderCustomizer customSerializationExclusionStrategy() { + return (gsonBuilder) -> gsonBuilder + .addSerializationExclusionStrategy(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes fieldAttributes) { + return "data".equals(fieldAttributes.getName()); + } + + @Override + public boolean shouldSkipClass(Class aClass) { + return false; + } + }); + } + } public class DataObject { @SuppressWarnings("unused") - private String data = "hello"; + public static final String STATIC_DATA = "bye"; + @SuppressWarnings("unused") + private Long data = 1L; + + public void setData(Long data) { + this.data = data; + } + } + + public class WrapperObject { + + @SuppressWarnings("unused") + class NestedObject { + @SuppressWarnings("unused") + private String data = "nested"; + } } } diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 9c5d4781c33..9a9cbed9fe6 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -340,6 +340,19 @@ content into your application. Rather, pick only the properties that you need. spring.jackson.serialization.*= # Jackson on/off features that affect the way Java objects are serialized. spring.jackson.time-zone= # Time zone used when formatting dates. For instance, "America/Los_Angeles" or "GMT+10". + # GSON ({sc-spring-boot-autoconfigure}/gson/GsonProperties.{sc-ext}[GsonProperties]) + spring.gson.date-format= # Configures Gson to serialize Date objects according to the pattern provided. + spring.gson.disable-html-escaping=false # By default, Gson escapes HTML characters such as < > etc. Use this option to configure Gson to pass-through HTML characters as is. + spring.gson.disable-inner-class-serialization=false # Configures Gson to exclude inner classes during serialization. + spring.gson.enable-complex-map-key-serialization=false # Enabling this feature will only change the serialized form if the map key is a complex type (i.e. non-primitive) in its serialized JSON form + spring.gson.exclude-fields-without-expose-annotation=false # Configures Gson to exclude all fields from consideration for serialization or deserialization that do not have the Expose annotation. + spring.gson.field-naming-policy= # Configures Gson to apply a specific naming policy to an object's field during serialization and deserialization. + spring.gson.generate-non-executable-json=false # Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some special text. + spring.gson.lenient=false # By default, Gson is strict and only accepts JSON as specified by RFC 4627. This option makes the parser liberal in what it accepts. + spring.gson.long-serialization-policy= # Configures Gson to apply a specific serialization policy for Long and long objects. + spring.gson.pretty-printing=false # Configures Gson to output Json that fits in a page for pretty printing. This option only affects Json serialization. + spring.gson.serialize-nulls=false # Configure Gson to serialize null fields. + # JERSEY ({sc-spring-boot-autoconfigure}/jersey/JerseyProperties.{sc-ext}[JerseyProperties]) spring.jersey.application-path= # Path that serves as the base URI for the application. If specified, overrides the value of "@ApplicationPath". spring.jersey.filter.order=0 # Jersey filter chain order.