Skip to content

Commit 14fe47e

Browse files
committed
Auto-configure SmileMapper.Builder and SmileMapper
Signed-off-by: Yanming Zhou <[email protected]>
1 parent 00bd0ef commit 14fe47e

File tree

5 files changed

+198
-1
lines changed

5 files changed

+198
-1
lines changed

module/spring-boot-jackson/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232
optional(project(":core:spring-boot-test-autoconfigure"))
3333
optional("org.springframework:spring-web")
3434
optional("tools.jackson.dataformat:jackson-dataformat-cbor")
35+
optional("tools.jackson.dataformat:jackson-dataformat-smile")
3536
optional("tools.jackson.dataformat:jackson-dataformat-xml")
3637
optional("tools.jackson.module:jackson-module-jakarta-xmlbind-annotations")
3738

module/spring-boot-jackson/src/main/java/org/springframework/boot/jackson/autoconfigure/JacksonAutoConfiguration.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import tools.jackson.databind.cfg.MapperBuilder;
4141
import tools.jackson.databind.json.JsonMapper;
4242
import tools.jackson.dataformat.cbor.CBORMapper;
43+
import tools.jackson.dataformat.smile.SmileMapper;
4344
import tools.jackson.dataformat.xml.XmlMapper;
4445

4546
import org.springframework.aot.hint.ReflectionHints;
@@ -81,6 +82,7 @@
8182
* @author Phillip Webb
8283
* @author Eddú Meléndez
8384
* @author Ralf Ueberfuhr
85+
* @author Yanming Zhou
8486
* @since 4.0.0
8587
*/
8688
@AutoConfiguration
@@ -237,6 +239,61 @@ public void customize(CBORMapper.Builder builder) {
237239

238240
}
239241

242+
@Configuration(proxyBeanMethods = false)
243+
@ConditionalOnClass(SmileMapper.class)
244+
@EnableConfigurationProperties(JacksonSmileProperties.class)
245+
static class SmileConfiguration {
246+
247+
@Bean
248+
@ConditionalOnMissingBean
249+
SmileMapper smileMapper(SmileMapper.Builder builder) {
250+
return builder.build();
251+
}
252+
253+
@Bean
254+
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
255+
@ConditionalOnMissingBean
256+
SmileMapper.Builder smileMapperBuilder(List<SmileMapperBuilderCustomizer> customizers) {
257+
SmileMapper.Builder builder = SmileMapper.builder();
258+
customize(builder, customizers);
259+
return builder;
260+
}
261+
262+
private void customize(SmileMapper.Builder builder, List<SmileMapperBuilderCustomizer> customizers) {
263+
for (SmileMapperBuilderCustomizer customizer : customizers) {
264+
customizer.customize(builder);
265+
}
266+
}
267+
268+
@Bean
269+
StandardSmileMapperBuilderCustomizer standardSmileMapperBuilderCustomizer(JacksonProperties jacksonProperties,
270+
ObjectProvider<JacksonModule> modules, JacksonSmileProperties smileProperties) {
271+
return new StandardSmileMapperBuilderCustomizer(jacksonProperties, modules.stream().toList(),
272+
smileProperties);
273+
}
274+
275+
static class StandardSmileMapperBuilderCustomizer extends AbstractMapperBuilderCustomizer<SmileMapper.Builder>
276+
implements SmileMapperBuilderCustomizer {
277+
278+
private final JacksonSmileProperties smileProperties;
279+
280+
StandardSmileMapperBuilderCustomizer(JacksonProperties jacksonProperties, Collection<JacksonModule> modules,
281+
JacksonSmileProperties smileProperties) {
282+
super(jacksonProperties, modules);
283+
this.smileProperties = smileProperties;
284+
}
285+
286+
@Override
287+
public void customize(SmileMapper.Builder builder) {
288+
super.customize(builder);
289+
configureFeatures(builder, this.smileProperties.getRead(), builder::configure);
290+
configureFeatures(builder, this.smileProperties.getWrite(), builder::configure);
291+
}
292+
293+
}
294+
295+
}
296+
240297
@Configuration(proxyBeanMethods = false)
241298
@ConditionalOnClass(XmlMapper.class)
242299
@EnableConfigurationProperties(JacksonXmlProperties.class)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.jackson.autoconfigure;
18+
19+
import java.util.EnumMap;
20+
import java.util.Map;
21+
22+
import tools.jackson.dataformat.smile.SmileReadFeature;
23+
import tools.jackson.dataformat.smile.SmileWriteFeature;
24+
25+
import org.springframework.boot.context.properties.ConfigurationProperties;
26+
27+
/**
28+
* Configuration properties to configure Jackson's Smile support.
29+
*
30+
* @author Yanming Zhou
31+
* @since 4.0.0
32+
*/
33+
@ConfigurationProperties("spring.jackson.smile")
34+
public class JacksonSmileProperties {
35+
36+
/**
37+
* Jackson on/off token reader features that are specific to Smile.
38+
*/
39+
private final Map<SmileReadFeature, Boolean> read = new EnumMap<>(SmileReadFeature.class);
40+
41+
/**
42+
* Jackson on/off token writer features that are specific to Smile.
43+
*/
44+
private final Map<SmileWriteFeature, Boolean> write = new EnumMap<>(SmileWriteFeature.class);
45+
46+
public Map<SmileReadFeature, Boolean> getRead() {
47+
return this.read;
48+
}
49+
50+
public Map<SmileWriteFeature, Boolean> getWrite() {
51+
return this.write;
52+
}
53+
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.jackson.autoconfigure;
18+
19+
import tools.jackson.dataformat.smile.SmileMapper;
20+
import tools.jackson.dataformat.smile.SmileMapper.Builder;
21+
22+
/**
23+
* Callback interface that can be implemented by beans wishing to further customize the
24+
* {@link SmileMapper} through {@link Builder SmileMapper.Builder} to fine-tune its
25+
* auto-configuration.
26+
*
27+
* @author Yanming Zhou
28+
* @since 4.0.0
29+
*/
30+
@FunctionalInterface
31+
public interface SmileMapperBuilderCustomizer {
32+
33+
/**
34+
* Customize the SmileMapper.Builder.
35+
* @param smileMapperBuilder the builder to customize
36+
*/
37+
void customize(Builder smileMapperBuilder);
38+
39+
}

module/spring-boot-jackson/src/test/java/org/springframework/boot/jackson/autoconfigure/JacksonAutoConfigurationTests.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import tools.jackson.databind.module.SimpleModule;
5858
import tools.jackson.databind.util.StdDateFormat;
5959
import tools.jackson.dataformat.cbor.CBORMapper;
60+
import tools.jackson.dataformat.smile.SmileMapper;
6061
import tools.jackson.dataformat.xml.XmlMapper;
6162
import tools.jackson.module.kotlin.KotlinModule;
6263

@@ -100,6 +101,7 @@
100101
* @author Grzegorz Poznachowski
101102
* @author Ralf Ueberfuhr
102103
* @author Eddú Meléndez
104+
* @author Yanming Zhou
103105
*/
104106
class JacksonAutoConfigurationTests {
105107

@@ -647,6 +649,16 @@ void cborMapperBuilderIsNotSharedAcrossMultipleInjectionPoints() {
647649
});
648650
}
649651

652+
@Test
653+
void smileMapperBuilderIsNotSharedAcrossMultipleInjectionPoints() {
654+
this.contextRunner.withUserConfiguration(SmileMapperBuilderConsumerConfig.class).run((context) -> {
655+
SmileMapperBuilderConsumerConfig consumer = context.getBean(SmileMapperBuilderConsumerConfig.class);
656+
assertThat(consumer.builderOne).isNotNull();
657+
assertThat(consumer.builderTwo).isNotNull();
658+
assertThat(consumer.builderOne).isNotSameAs(consumer.builderTwo);
659+
});
660+
}
661+
650662
@Test
651663
void xmlMapperBuilderIsNotSharedAcrossMultipleInjectionPoints() {
652664
this.contextRunner.withUserConfiguration(XmlMapperBuilderConsumerConfig.class).run((context) -> {
@@ -725,6 +737,19 @@ void whenUsingJackson2DefaultsCborMapperShouldBeConfiguredUsingConfigureForJacks
725737
});
726738
}
727739

740+
@Test
741+
void whenUsingJackson2DefaultsSmileMapperShouldBeConfiguredUsingConfigureForJackson2() {
742+
this.contextRunner.withPropertyValues("spring.jackson.use-jackson2-defaults=true").run((context) -> {
743+
SmileMapper smileMapper = context.getBean(SmileMapper.class);
744+
SmileMapper jackson2ConfiguredSmileMapper = SmileMapper.builder().configureForJackson2().build();
745+
assertCommonFeatureConfiguration(smileMapper, jackson2ConfiguredSmileMapper);
746+
assertThat(smileMapper.deserializationConfig().getFormatReadFeatures())
747+
.isEqualTo(jackson2ConfiguredSmileMapper.deserializationConfig().getFormatReadFeatures());
748+
assertThat(smileMapper.serializationConfig().getFormatWriteFeatures())
749+
.isEqualTo(jackson2ConfiguredSmileMapper.serializationConfig().getFormatWriteFeatures());
750+
});
751+
}
752+
728753
@Test
729754
void whenUsingJackson2DefaultsXmlMapperShouldBeConfiguredUsingConfigureForJackson2() {
730755
this.contextRunner.withPropertyValues("spring.jackson.use-jackson2-defaults=true").run((context) -> {
@@ -969,6 +994,27 @@ String consumerTwo(CBORMapper.Builder builder) {
969994

970995
}
971996

997+
@Configuration(proxyBeanMethods = false)
998+
static class SmileMapperBuilderConsumerConfig {
999+
1000+
SmileMapper.@Nullable Builder builderOne;
1001+
1002+
SmileMapper.@Nullable Builder builderTwo;
1003+
1004+
@Bean
1005+
String consumerOne(SmileMapper.Builder builder) {
1006+
this.builderOne = builder;
1007+
return "one";
1008+
}
1009+
1010+
@Bean
1011+
String consumerTwo(SmileMapper.Builder builder) {
1012+
this.builderTwo = builder;
1013+
return "two";
1014+
}
1015+
1016+
}
1017+
9721018
@Configuration(proxyBeanMethods = false)
9731019
static class XmlMapperBuilderConsumerConfig {
9741020

@@ -1114,7 +1160,7 @@ static class CircularDependencySerializerConfiguration {
11141160
enum MapperType {
11151161

11161162
CBOR(CBORMapper.class, CBORMapper.Builder.class), JSON(JsonMapper.class, JsonMapper.Builder.class),
1117-
XML(XmlMapper.class, XmlMapper.Builder.class);
1163+
SMILE(SmileMapper.class, SmileMapper.Builder.class), XML(XmlMapper.class, XmlMapper.Builder.class);
11181164

11191165
private final Class<? extends ObjectMapper> mapperClass;
11201166

0 commit comments

Comments
 (0)