-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(#4218): avoid executing the injection logic multiple times #5201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5aaa7ad
66966ca
8277460
ac086da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
import java.text.DateFormat; | ||
import java.text.ParseException; | ||
import java.util.*; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
|
||
import com.fasterxml.jackson.annotation.JacksonInject; | ||
|
@@ -70,6 +71,12 @@ public abstract class DeserializationContext | |
*/ | ||
protected final DeserializerCache _cache; | ||
|
||
/** | ||
* Object that caches the injectable values already resolved, | ||
* to avoid executing the injection logic multiple times | ||
*/ | ||
protected final Map<Object, Object> _injectablesCache; | ||
|
||
/* | ||
/********************************************************** | ||
/* Configuration, changeable via fluent factories | ||
|
@@ -169,6 +176,7 @@ protected DeserializationContext(DeserializerFactory df, | |
cache = new DeserializerCache(); | ||
} | ||
_cache = cache; | ||
_injectablesCache = new ConcurrentHashMap<>(); | ||
_featureFlags = 0; | ||
_readCapabilities = null; | ||
_config = null; | ||
|
@@ -181,6 +189,7 @@ protected DeserializationContext(DeserializationContext src, | |
DeserializerFactory factory) | ||
{ | ||
_cache = src._cache; | ||
_injectablesCache = src._injectablesCache; | ||
_factory = factory; | ||
|
||
_config = src._config; | ||
|
@@ -199,6 +208,7 @@ protected DeserializationContext(DeserializationContext src, | |
DeserializerCache cache) | ||
{ | ||
_cache = cache; | ||
_injectablesCache = src._injectablesCache; | ||
_factory = src._factory; | ||
|
||
_config = src._config; | ||
|
@@ -218,6 +228,7 @@ protected DeserializationContext(DeserializationContext src, | |
InjectableValues injectableValues) | ||
{ | ||
_cache = src._cache; | ||
_injectablesCache = src._injectablesCache; | ||
_factory = src._factory; | ||
// 08-Jun-2020. tatu: Called only for `ObjectMapper.canDeserialize()` | ||
// (see [databind#2749]), not sure what's the best work-around but | ||
|
@@ -242,6 +253,7 @@ protected DeserializationContext(DeserializationContext src, | |
DeserializationConfig config) | ||
{ | ||
_cache = src._cache; | ||
_injectablesCache = src._injectablesCache; | ||
_factory = src._factory; | ||
_readCapabilities = null; | ||
|
||
|
@@ -259,6 +271,7 @@ protected DeserializationContext(DeserializationContext src, | |
*/ | ||
protected DeserializationContext(DeserializationContext src) { | ||
_cache = src._cache.emptyCopy(); | ||
_injectablesCache = src._injectablesCache; | ||
_factory = src._factory; | ||
|
||
_config = src._config; | ||
|
@@ -479,7 +492,19 @@ public final Object findInjectableValue(Object valueId, | |
"No 'injectableValues' configured, cannot inject value with id '%s'", valueId), | ||
valueId, forProperty, beanInstance); | ||
} | ||
return _injectableValues.findInjectableValue(this, valueId, forProperty, beanInstance, optional); | ||
|
||
Object value = _injectablesCache.get(valueId); | ||
|
||
if (value == null) { | ||
value = _injectableValues.findInjectableValue(this, valueId, forProperty, beanInstance, | ||
optional); | ||
|
||
if (value != null) { | ||
_injectablesCache.put(valueId, value); | ||
} | ||
} | ||
|
||
return value; | ||
Comment on lines
+495
to
+507
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this make injectino logic to run exactly once? Or less than before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly once, as proven by this test, which doesn't fail anymore with the change above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that is true: while locating the value only occurs just once, wouldn't actual injection occur multiple times still? While this results in same value being used, it still calls injection (pass via constructor, set field) more than once. Ideally I'd rather neither would happen more than once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. The value is either found in cache or actually resolved, but the injection happens anyway. |
||
} | ||
|
||
/** | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need not be thread-safe, single deserialization calls are never concurrent. So simple
HashMap
would do.Single
DeserializationContext
is only ever used for singlereadValue()
call.Also: should not eagerly construct
Map
since this imposes cost for all usage, even when (like in most cases I'd guess) no injection is required.So should instead lazily construct when needed.
All of this assuming we actually want caching. Not sold on that yet either.