Skip to content

Commit 34966f4

Browse files
authored
Fixes #5093: make BeanDescription construction lazy to defer/avoid some introspection (#5095)
1 parent 512509b commit 34966f4

19 files changed

+306
-236
lines changed

release-notes/VERSION

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ Versions: 3.x (for earlier see VERSION-2.x)
55
=== Releases ===
66
------------------------------------------------------------------------
77

8+
3.0.0-rc4 (not yet released)
9+
10+
#5093: Change the way `BeanDescription` passed during serializer construction
11+
to use `Supplier`
12+
813
3.0.0-rc3 (13-Apr-2025)
914

1015
#4603: Keep full stacktrace when re-throwing exception with

src/main/java/tools/jackson/databind/BeanDescription.java

+35
Original file line numberDiff line numberDiff line change
@@ -298,4 +298,39 @@ public AnnotatedMember findJsonKeyAccessor() {
298298
* global default settings.
299299
*/
300300
public abstract Class<?>[] findDefaultViews();
301+
302+
303+
/**
304+
* Base implementation for lazily-constructed suppliers for {@link BeanDescription} instances.
305+
*/
306+
public static abstract class Supplier implements java.util.function.Supplier<BeanDescription>
307+
{
308+
private final JavaType _type;
309+
310+
private transient BeanDescription _beanDesc;
311+
312+
protected Supplier(JavaType type) {
313+
_type = type;
314+
}
315+
316+
public JavaType getType() { return _type; }
317+
318+
public Class<?> getBeanClass() { return _type.getRawClass(); }
319+
320+
public boolean isRecordType() { return _type.isRecordType(); }
321+
322+
public AnnotatedClass getClassInfo() {
323+
return get().getClassInfo();
324+
}
325+
326+
@Override
327+
public BeanDescription get() {
328+
if (_beanDesc == null) {
329+
_beanDesc = _construct(_type);
330+
}
331+
return _beanDesc;
332+
}
333+
334+
protected abstract BeanDescription _construct(JavaType forType);
335+
}
301336
}

src/main/java/tools/jackson/databind/DatabindContext.java

+15
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,15 @@ protected abstract DatabindException invalidTypeIdException(JavaType baseType, S
312312
*/
313313
public abstract BeanDescription introspectBeanDescription(JavaType type);
314314

315+
public BeanDescription.Supplier lazyIntrospectBeanDescription(JavaType type) {
316+
return new BeanDescription.Supplier(type) {
317+
@Override
318+
public BeanDescription _construct(JavaType forType) {
319+
return introspectBeanDescription(forType);
320+
}
321+
};
322+
}
323+
315324
public AnnotatedClass introspectClassAnnotations(JavaType type) {
316325
return classIntrospector().introspectClassAnnotations(type);
317326
}
@@ -430,6 +439,12 @@ public abstract <T> T reportBadTypeDefinition(BeanDescription bean,
430439
String msg, Object... msgArgs)
431440
throws DatabindException;
432441

442+
public <T> T reportBadTypeDefinition(BeanDescription.Supplier beanDescRef,
443+
String msg, Object... msgArgs)
444+
throws DatabindException {
445+
return reportBadTypeDefinition(beanDescRef.get(), msg, msgArgs);
446+
}
447+
433448
/*
434449
/**********************************************************************
435450
/* Helper methods

src/main/java/tools/jackson/databind/SerializationContext.java

+14-14
Original file line numberDiff line numberDiff line change
@@ -959,13 +959,13 @@ protected ValueSerializer<Object> _createAndCacheUntypedSerializer(Class<?> rawT
959959
JavaType fullType)
960960
{
961961
// Important: must introspect all annotations, not just class
962-
BeanDescription beanDesc = introspectBeanDescription(fullType);
962+
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(fullType);
963963
ValueSerializer<Object> ser;
964964
try {
965-
ser = _serializerFactory.createSerializer(this, fullType, beanDesc, null);
965+
ser = _serializerFactory.createSerializer(this, fullType, beanDescRef, null);
966966
} catch (IllegalArgumentException iae) {
967967
// We better only expose checked exceptions, since those are what caller is expected to handle
968-
reportBadTypeDefinition(beanDesc, ClassUtil.exceptionMessage(iae));
968+
reportBadTypeDefinition(beanDescRef, ClassUtil.exceptionMessage(iae));
969969
ser = null; // never gets here
970970
}
971971
// Always cache -- and in this case both for raw and full type
@@ -976,10 +976,10 @@ protected ValueSerializer<Object> _createAndCacheUntypedSerializer(Class<?> rawT
976976
protected ValueSerializer<Object> _createAndCacheUntypedSerializer(JavaType type)
977977
{
978978
// Important: must introspect all annotations, not just class
979-
BeanDescription beanDesc = introspectBeanDescription(type);
979+
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(type);
980980
ValueSerializer<Object> ser;
981981
try {
982-
ser = _serializerFactory.createSerializer(this, type, beanDesc, null);
982+
ser = _serializerFactory.createSerializer(this, type, beanDescRef, null);
983983
} catch (IllegalArgumentException iae) {
984984
// We better only expose checked exceptions, since those are what caller is expected to handle
985985
throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae));
@@ -996,10 +996,10 @@ protected ValueSerializer<Object> _createAndCacheUntypedSerializer(JavaType type
996996
protected ValueSerializer<Object> _createAndCachePropertySerializer(Class<?> rawType,
997997
JavaType fullType, BeanProperty prop)
998998
{
999-
BeanDescription beanDesc = introspectBeanDescription(fullType);
999+
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(fullType);
10001000
ValueSerializer<Object> ser;
10011001
try {
1002-
ser = _serializerFactory.createSerializer(this, fullType, beanDesc, null);
1002+
ser = _serializerFactory.createSerializer(this, fullType, beanDescRef, null);
10031003
} catch (IllegalArgumentException iae) {
10041004
throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae));
10051005
}
@@ -1008,7 +1008,7 @@ protected ValueSerializer<Object> _createAndCachePropertySerializer(Class<?> raw
10081008
if (prop == null) {
10091009
return ser;
10101010
}
1011-
return _checkShapeShifting(fullType, beanDesc, prop, ser);
1011+
return _checkShapeShifting(fullType, beanDescRef, prop, ser);
10121012
}
10131013

10141014
/**
@@ -1018,10 +1018,10 @@ protected ValueSerializer<Object> _createAndCachePropertySerializer(Class<?> raw
10181018
protected ValueSerializer<Object> _createAndCachePropertySerializer(JavaType type,
10191019
BeanProperty prop)
10201020
{
1021-
BeanDescription beanDesc = introspectBeanDescription(type);
1021+
BeanDescription.Supplier beanDescRef = lazyIntrospectBeanDescription(type);
10221022
ValueSerializer<Object> ser;
10231023
try {
1024-
ser = _serializerFactory.createSerializer(this, type, beanDesc, null);
1024+
ser = _serializerFactory.createSerializer(this, type, beanDescRef, null);
10251025
} catch (IllegalArgumentException iae) {
10261026
throw _mappingProblem(iae, ClassUtil.exceptionMessage(iae));
10271027
}
@@ -1030,12 +1030,12 @@ protected ValueSerializer<Object> _createAndCachePropertySerializer(JavaType typ
10301030
if (prop == null) {
10311031
return ser;
10321032
}
1033-
return _checkShapeShifting(type, beanDesc, prop, ser);
1033+
return _checkShapeShifting(type, beanDescRef, prop, ser);
10341034
}
10351035

10361036
@SuppressWarnings("unchecked")
1037-
private ValueSerializer<Object> _checkShapeShifting(JavaType type, BeanDescription beanDesc,
1038-
BeanProperty prop, ValueSerializer<?> ser)
1037+
private ValueSerializer<Object> _checkShapeShifting(JavaType type,
1038+
BeanDescription.Supplier beanDescRef, BeanProperty prop, ValueSerializer<?> ser)
10391039
{
10401040
JsonFormat.Value overrides = prop.findFormatOverrides(_config);
10411041
if (overrides != null) {
@@ -1045,7 +1045,7 @@ private ValueSerializer<Object> _checkShapeShifting(JavaType type, BeanDescripti
10451045
ser = ser2;
10461046
} else {
10471047
// But if not, we need to re-create it via factory
1048-
ser = _serializerFactory.createSerializer(this, type, beanDesc, overrides);
1048+
ser = _serializerFactory.createSerializer(this, type, beanDescRef, overrides);
10491049
}
10501050
}
10511051
return (ValueSerializer<Object>) ser;

src/main/java/tools/jackson/databind/ext/javatime/ser/JavaTimeSerializerModifier.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public JavaTimeSerializerModifier() { }
1212

1313
@Override
1414
public ValueSerializer<?> modifyEnumSerializer(SerializationConfig config, JavaType valueType,
15-
BeanDescription beanDesc, ValueSerializer<?> serializer) {
15+
BeanDescription.Supplier beanDesc, ValueSerializer<?> serializer) {
1616
if (valueType.hasRawClass(Month.class)) {
1717
return new OneBasedMonthSerializer(serializer);
1818
}

src/main/java/tools/jackson/databind/module/SimpleSerializers.java

+19-11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import tools.jackson.databind.type.CollectionType;
1414
import tools.jackson.databind.type.MapLikeType;
1515
import tools.jackson.databind.type.MapType;
16+
import tools.jackson.databind.type.ReferenceType;
1617

1718
/**
1819
* Simple implementation {@link Serializers} which allows registration of
@@ -102,7 +103,7 @@ public SimpleSerializers addSerializers(List<ValueSerializer<?>> sers) {
102103

103104
@Override
104105
public ValueSerializer<?> findSerializer(SerializationConfig config,
105-
JavaType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides)
106+
JavaType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides)
106107
{
107108
Class<?> cls = type.getRawClass();
108109
ClassKey key = new ClassKey(cls);
@@ -163,39 +164,46 @@ public ValueSerializer<?> findSerializer(SerializationConfig config,
163164

164165
@Override
165166
public ValueSerializer<?> findArraySerializer(SerializationConfig config,
166-
ArrayType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides,
167+
ArrayType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
167168
TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
168-
return findSerializer(config, type, beanDesc, formatOverrides);
169+
return findSerializer(config, type, beanDescRef, formatOverrides);
169170
}
170171

171172
@Override
172173
public ValueSerializer<?> findCollectionSerializer(SerializationConfig config,
173-
CollectionType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides,
174+
CollectionType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
174175
TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
175-
return findSerializer(config, type, beanDesc, formatOverrides);
176+
return findSerializer(config, type, beanDescRef, formatOverrides);
176177
}
177178

178179
@Override
179180
public ValueSerializer<?> findCollectionLikeSerializer(SerializationConfig config,
180-
CollectionLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides,
181+
CollectionLikeType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
181182
TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
182-
return findSerializer(config, type, beanDesc, formatOverrides);
183+
return findSerializer(config, type, beanDescRef, formatOverrides);
183184
}
184185

185186
@Override
186187
public ValueSerializer<?> findMapSerializer(SerializationConfig config,
187-
MapType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides,
188+
MapType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
188189
ValueSerializer<Object> keySerializer,
189190
TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
190-
return findSerializer(config, type, beanDesc, formatOverrides);
191+
return findSerializer(config, type, beanDescRef, formatOverrides);
191192
}
192193

193194
@Override
194195
public ValueSerializer<?> findMapLikeSerializer(SerializationConfig config,
195-
MapLikeType type, BeanDescription beanDesc, JsonFormat.Value formatOverrides,
196+
MapLikeType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
196197
ValueSerializer<Object> keySerializer,
197198
TypeSerializer elementTypeSerializer, ValueSerializer<Object> elementValueSerializer) {
198-
return findSerializer(config, type, beanDesc, formatOverrides);
199+
return findSerializer(config, type, beanDescRef, formatOverrides);
200+
}
201+
202+
@Override
203+
public ValueSerializer<?> findReferenceSerializer(SerializationConfig config,
204+
ReferenceType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
205+
TypeSerializer contentTypeSerializer, ValueSerializer<Object> contentValueSerializer) {
206+
return findSerializer(config, type, beanDescRef, formatOverrides);
199207
}
200208

201209
/*

0 commit comments

Comments
 (0)