關鍵詞
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.PageResult` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
版本信息
升級前版本
SpringBoot | SpringCloud |
2.0.6.RELEASE | Finchley.SR2 |
升級後版本
SpringBoot | SpringCloud |
2.2.5.RELEASE | Hoxton.SR3 |
錯誤內容(較長)
com.netflix.hystrix.exception.HystrixRuntimeException: ItemTemplateApiClient#searchAll(ItemTemplateSearchVo) failed and no fallback available.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:822)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:807)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472)
at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1397)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.observers.Subscribers$5.onError(Subscribers.java:230)
at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:44)
at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:28)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:142)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$3.onError(AbstractCommand.java:1194)
at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.onError(OperatorSubscribeOn.java:80)
at rx.observers.Subscribers$5.onError(Subscribers.java:230)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.observers.Subscribers$5.onError(Subscribers.java:230)
at com.netflix.hystrix.AbstractCommand$DeprecatedOnRunHookApplication$1.onError(AbstractCommand.java:1431)
at com.netflix.hystrix.AbstractCommand$ExecutionHookApplication$1.onError(AbstractCommand.java:1362)
at rx.observers.Subscribers$5.onError(Subscribers.java:230)
at rx.observers.Subscribers$5.onError(Subscribers.java:230)
at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:44)
at rx.internal.operators.OnSubscribeThrow.call(OnSubscribeThrow.java:28)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47)
at org.springframework.cloud.sleuth.instrument.async.TraceCallable.call(TraceCallable.java:70)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: feign.codec.DecodeException: Type definition error: [simple type, class xxx.PageResult]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.PageResult` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 8027] (through reference chain: xxx.Result["response"])
at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:187)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:147)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:80)
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:109)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
... 28 common frames omitted
Caused by: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class xxx.PageResult]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.PageResult` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 8027] (through reference chain: xxx.Result["response"])
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:246)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:228)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:104)
at org.springframework.cloud.openfeign.support.SpringDecoder.decode(SpringDecoder.java:59)
at org.springframework.cloud.openfeign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:62)
at feign.optionals.OptionalDecoder.decode(OptionalDecoder.java:36)
at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:183)
... 34 common frames omitted
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.PageResult` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 8027] (through reference chain: xxx.Result["response"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1589)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1055)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3258)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:240)
... 40 common frames omitted
錯誤原因
很明顯說我的PageResult沒有構造函數, 我給PageResult加上構造函數即可
但是爲什麼升級之前又可以呢?
源碼分析(jackson)
這裏我繞了點彎路, 我去分析了jackson反序列化的源碼, 但最後發現根本就不是jackson的問題,而是lombok的問題
jackson的調試有點艱辛, 僅貼出部分追蹤到的核心代碼
首先已知我的Fegin接口返回類型爲Result<PageResult<ItemVo>>, 屬於多層泛型, 問題就在中間PageResult無法反序列化
第一階段分析到處理對象實例化的代碼,和設置對象屬性的核心代碼
com.fasterxml.jackson.databind.deser.BeanDeserializer#deserializeFromObject
/**
* General version used when handling needs more advanced features.
*/
// 本方法會遞歸執行, 將對象所有層次的值設置完
@Override
public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException
{
/* 09-Dec-2014, tatu: As per [databind#622], we need to allow Object Id references
* to come in as JSON Objects as well; but for now assume they will
* be simple, single-property references, which means that we can
* recognize them without having to buffer anything.
* Once again, if we must, we can do more complex handling with buffering,
* but let's only do that if and when that becomes necessary.
*/
if ((_objectIdReader != null) && _objectIdReader.maySerializeAsObject()) {
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)
&& _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) {
return deserializeFromObjectId(p, ctxt);
}
}
if (_nonStandardCreation) {
if (_unwrappedPropertyHandler != null) {
return deserializeWithUnwrapped(p, ctxt);
}
if (_externalTypeIdHandler != null) {
return deserializeWithExternalTypeId(p, ctxt);
}
Object bean = deserializeFromObjectUsingNonDefault(p, ctxt);
if (_injectables != null) {
injectValues(ctxt, bean);
}
/* 27-May-2014, tatu: I don't think view processing would work
* at this point, so commenting it out; but leaving in place
* just in case I forgot something fundamental...
*/
/*
if (_needViewProcesing) {
Class<?> view = ctxt.getActiveView();
if (view != null) {
return deserializeWithView(p, ctxt, bean, view);
}
}
*/
return bean;
}
// 這裏就是創建對象的方法調用
final Object bean = _valueInstantiator.createUsingDefault(ctxt);
// [databind#631]: Assign current value, to be accessible by custom deserializers
p.setCurrentValue(bean);
if (p.canReadObjectId()) {
Object id = p.getObjectId();
if (id != null) {
_handleTypedObjectId(p, ctxt, bean, id);
}
}
if (_injectables != null) {
injectValues(ctxt, bean);
}
if (_needViewProcesing) {
Class<?> view = ctxt.getActiveView();
if (view != null) {
return deserializeWithView(p, ctxt, bean, view);
}
}
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
// 這裏是獲得接口返回json中key值
String propName = p.getCurrentName();
do {
p.nextToken();
// 這裏尋找創建出來的Bean對象對應json的key的字段描述
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
// 這裏就是將值設置到Bean字段裏, 如果Bean的字段類型爲泛型, 內部繼續遞歸的調用本方法, 直到遞歸完所有層次的字段值(樹狀遞歸)
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
handleUnknownVanilla(p, ctxt, bean, propName);
} while ((propName = p.nextFieldName()) != null);
}
return bean;
}
創建Bean的方法
com.fasterxml.jackson.databind.deser.std.StdValueInstantiator#createUsingDefault
@Override
public Object createUsingDefault(DeserializationContext ctxt) throws IOException
{
// 判斷是否有默認構造函數
if (_defaultCreator == null) { // sanity-check; caller should check
// 沒有無參構造拋異常
return super.createUsingDefault(ctxt);
}
try {
// 有無參構造則調用創建Bean
return _defaultCreator.call();
} catch (Exception e) { // 19-Apr-2017, tatu: Let's not catch Errors, just Exceptions
return ctxt.handleInstantiationProblem(_valueClass, null, rewrapCtorProblem(ctxt, e));
}
}
然後去分析_defaultCreator 屬性是從哪裏賦值的
com.fasterxml.jackson.databind.deser.std.StdValueInstantiator#StdValueInstantiator(com.fasterxml.jackson.databind.deser.std.StdValueInstantiator)
protected StdValueInstantiator(StdValueInstantiator src)
{
_valueTypeDesc = src._valueTypeDesc;
_valueClass = src._valueClass;
// 這裏傳進來的
_defaultCreator = src._defaultCreator;
_constructorArguments = src._constructorArguments;
_withArgsCreator = src._withArgsCreator;
_delegateType = src._delegateType;
_delegateCreator = src._delegateCreator;
_delegateArguments = src._delegateArguments;
_arrayDelegateType = src._arrayDelegateType;
_arrayDelegateCreator = src._arrayDelegateCreator;
_arrayDelegateArguments = src._arrayDelegateArguments;
_fromStringCreator = src._fromStringCreator;
_fromIntCreator = src._fromIntCreator;
_fromLongCreator = src._fromLongCreator;
_fromDoubleCreator = src._fromDoubleCreator;
_fromBooleanCreator = src._fromBooleanCreator;
}
在這裏下斷點後,向調用棧向上找, 然後發現居然來自緩存= =!!
com.fasterxml.jackson.databind.deser.DeserializerCache#_cachedDeserializers
final protected LRUMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers;
然後分析VO描述是怎麼存到緩存裏的
com.fasterxml.jackson.databind.deser.DeserializerCache#_createDeserializer
/**
* Method that does the heavy lifting of checking for per-type annotations,
* find out full type, and figure out which actual factory method
* to call.
*/
@SuppressWarnings("unchecked")
protected JsonDeserializer<Object> _createDeserializer(DeserializationContext ctxt,
DeserializerFactory factory, JavaType type)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
// First things first: do we need to use abstract type mapping?
if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) {
type = factory.mapAbstractType(config, type);
}
// 這裏會先初始化類的描述信息, 但是還沒有觸發找構造函數的代碼
BeanDescription beanDesc = config.introspect(type);
// Then: does type define explicit deserializer to use, with annotation(s)?
JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt,
beanDesc.getClassInfo());
if (deser != null) {
return deser;
}
// If not, may have further type-modification annotations to check:
JavaType newType = modifyTypeByAnnotation(ctxt, beanDesc.getClassInfo(), type);
if (newType != type) {
type = newType;
beanDesc = config.introspect(newType);
}
// We may also have a Builder type to consider...
Class<?> builder = beanDesc.findPOJOBuilder();
if (builder != null) {
return (JsonDeserializer<Object>) factory.createBuilderBasedDeserializer(
ctxt, type, beanDesc, builder);
}
// Or perhaps a Converter?
Converter<Object,Object> conv = beanDesc.findDeserializationConverter();
if (conv == null) { // nope, just construct in normal way
// 從此處初始化, 傳入類的描述信息
return (JsonDeserializer<Object>) _createDeserializer2(ctxt, factory, type, beanDesc);
}
// otherwise need to do bit of introspection
JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
// One more twist, as per [databind#288]; probably need to get new BeanDesc
if (!delegateType.hasRawClass(type.getRawClass())) {
beanDesc = config.introspect(delegateType);
}
return new StdDelegatingDeserializer<Object>(conv, delegateType,
_createDeserializer2(ctxt, factory, delegateType, beanDesc));
}
省略一下步驟...(有點多), 而且都不重要, 都是一直向下傳遞beanDesc這個對象
com.fasterxml.jackson.databind.deser.BasicDeserializerFactory#_constructDefaultValueInstantiator
/**
* Method that will construct standard default {@link ValueInstantiator}
* using annotations (like @JsonCreator) and visibility rules
*/
protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationContext ctxt,
BeanDescription beanDesc)
throws JsonMappingException
{
CreatorCollector creators = new CreatorCollector(beanDesc, ctxt.getConfig());
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
// need to construct suitable visibility checker:
final DeserializationConfig config = ctxt.getConfig();
VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker(beanDesc.getBeanClass(),
beanDesc.getClassInfo());
/* 24-Sep-2014, tatu: Tricky part first; need to merge resolved property information
* (which has creator parameters sprinkled around) with actual creator
* declarations (which are needed to access creator annotation, amongst other things).
* Easiest to combine that info first, then pass it to remaining processing.
*/
/* 15-Mar-2015, tatu: Alas, this won't help with constructors that only have implicit
* names. Those will need to be resolved later on.
*/
Map<AnnotatedWithParams,BeanPropertyDefinition[]> creatorDefs = _findCreatorsFromProperties(ctxt,
beanDesc);
// Important: first add factory methods; then constructors, so
// latter can override former!
// 添加工廠方法, 這裏邊觸發的找構造方法的代碼
_addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators, creatorDefs);
// constructors only usable on concrete types:
if (beanDesc.getType().isConcrete()) {
// 設置一下要調用的(無參)構造方法
_addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators, creatorDefs);
}
// 調用構造方法初始化對象
return creators.constructValueInstantiator(ctxt);
}
然後一堆代碼, 都不重要, 看到代碼
com.fasterxml.jackson.databind.deser.BasicDeserializerFactory#_addDeserializerFactoryMethods
// 觸發點在beanDesc.getFactoryMethods()
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
...
}
com.fasterxml.jackson.databind.introspect.BasicBeanDescription#getFactoryMethods
/*
/**********************************************************
/* Introspection for deserialization, factories
/**********************************************************
*/
@Override
public List<AnnotatedMethod> getFactoryMethods()
{
// must filter out anything that clearly is not a factory method
// 這裏獲得所有構造方法
List<AnnotatedMethod> candidates = _classInfo.getFactoryMethods();
if (candidates.isEmpty()) {
return candidates;
}
List<AnnotatedMethod> result = null;
for (AnnotatedMethod am : candidates) {
if (isFactoryMethod(am)) {
if (result == null) {
result = new ArrayList<AnnotatedMethod>();
}
result.add(am);
}
}
if (result == null) {
return Collections.emptyList();
}
return result;
}
com.fasterxml.jackson.databind.introspect.AnnotatedClass#getFactoryMethods
public List<AnnotatedMethod> getFactoryMethods() {
return _creators().creatorMethods;
}
嗯裏邊是懶加載的方式初始化的, 初始化過一次後就保存到成員變量裏
com.fasterxml.jackson.databind.introspect.AnnotatedClass#_creators
private final Creators _creators() {
Creators c = _creators;
if (c == null) {
if (_type == null) {
c = NO_CREATORS;
} else {
// 這裏初始化該類的所有構造方法的調用描述, 包括有參和無參構造
c = AnnotatedCreatorCollector.collectCreators(_annotationIntrospector,
this, _type, _primaryMixIn);
}
_creators = c;
}
return c;
}
com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector#collectCreators
public static Creators collectCreators(AnnotationIntrospector intr,
TypeResolutionContext tc,
JavaType type, Class<?> primaryMixIn)
{
// Constructor also always members of resolved class, parent == resolution context
return new AnnotatedCreatorCollector(intr, tc)
// 代碼在這裏
.collect(type, primaryMixIn);
}
Creators collect(JavaType type, Class<?> primaryMixIn)
{
// 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does
// NOT apply to context since sub-class may have type bindings
// TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings());
// 獲取所有構造方法
List<AnnotatedConstructor> constructors = _findPotentialConstructors(type, primaryMixIn);
List<AnnotatedMethod> factories = _findPotentialFactories(type, primaryMixIn);
/* And then... let's remove all constructors that are deemed
* ignorable after all annotations have been properly collapsed.
*/
// AnnotationIntrospector is null if annotations not enabled; if so, can skip:
if (_intr != null) {
if (_defaultConstructor != null) {
if (_intr.hasIgnoreMarker(_defaultConstructor)) {
_defaultConstructor = null;
}
}
// count down to allow safe removal
for (int i = constructors.size(); --i >= 0; ) {
if (_intr.hasIgnoreMarker(constructors.get(i))) {
constructors.remove(i);
}
}
for (int i = factories.size(); --i >= 0; ) {
if (_intr.hasIgnoreMarker(factories.get(i))) {
factories.remove(i);
}
}
}
return new AnnotatedClass.Creators(_defaultConstructor, constructors, factories);
}
這裏就是對類的所有構造方法封裝成描述對象的方法了
com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector#_findPotentialConstructors
/**
* Helper method for locating constructors (and matching mix-in overrides)
* we might want to use; this is needed in order to mix information between
* the two and construct resulting {@link AnnotatedConstructor}s
*/
private List<AnnotatedConstructor> _findPotentialConstructors(JavaType type,
Class<?> primaryMixIn)
{
ClassUtil.Ctor defaultCtor = null;
List<ClassUtil.Ctor> ctors = null;
// 18-Jun-2016, tatu: Enum constructors will never be useful (unlike
// possibly static factory methods); but they can be royal PITA
// due to some oddities by JVM; see:
// [https://github.com/FasterXML/jackson-module-parameter-names/issues/35]
// for more. So, let's just skip them.
// 判斷類是否不爲枚舉類
if (!type.isEnumType()) {
// 通過工具類獲取該類的所有構造方法
ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(type.getRawClass());
// 一下創建內部維護的構造函數描述成員變量
for (ClassUtil.Ctor ctor : declaredCtors) {
if (!isIncludableConstructor(ctor.getConstructor())) {
continue;
}
if (ctor.getParamCount() == 0) {
// 參數數量爲0個的設爲無參的默認構造器
defaultCtor = ctor;
} else {
if (ctors == null) {
ctors = new ArrayList<>();
}
ctors.add(ctor);
}
}
}
List<AnnotatedConstructor> result;
int ctorCount;
if (ctors == null) {
result = Collections.emptyList();
// Nothing found? Short-circuit
if (defaultCtor == null) {
return result;
}
ctorCount = 0;
} else {
ctorCount = ctors.size();
result = new ArrayList<>(ctorCount);
for (int i = 0; i < ctorCount; ++i) {
result.add(null);
}
}
// primaryMixIn 爲 null, 這裏不會執行
// so far so good; but do we also need to find mix-ins overrides?
if (primaryMixIn != null) {
MemberKey[] ctorKeys = null;
for (ClassUtil.Ctor mixinCtor : ClassUtil.getConstructors(primaryMixIn)) {
if (mixinCtor.getParamCount() == 0) {
if (defaultCtor != null) {
_defaultConstructor = constructDefaultConstructor(defaultCtor, mixinCtor);
defaultCtor = null;
}
continue;
}
if (ctors != null) {
if (ctorKeys == null) {
ctorKeys = new MemberKey[ctorCount];
for (int i = 0; i < ctorCount; ++i) {
ctorKeys[i] = new MemberKey(ctors.get(i).getConstructor());
}
}
MemberKey key = new MemberKey(mixinCtor.getConstructor());
for (int i = 0; i < ctorCount; ++i) {
if (key.equals(ctorKeys[i])) {
result.set(i,
constructNonDefaultConstructor(ctors.get(i), mixinCtor));
break;
}
}
}
}
}
// Ok: anything within mix-ins has been resolved; anything remaining we must resolve
if (defaultCtor != null) {
// 這裏就會設置默認的無參構造器, 然後供後續操作初始化該類, 如果上邊沒有做找到defaultCtor的話_defaultConstructor就會是null, 後邊jackson返序列化就會報無默認構造器異常
_defaultConstructor = constructDefaultConstructor(defaultCtor, null);
}
for (int i = 0; i < ctorCount; ++i) {
AnnotatedConstructor ctor = result.get(i);
if (ctor == null) {
result.set(i,
constructNonDefaultConstructor(ctors.get(i), null));
}
}
return result;
}
看看他獲取構造器的工具類代碼, 很簡單就是用jdkClass提供的getDeclaredConstructors()方法獲取所有構造方法,包括私有的然後封裝成自定義的對象就返回了
com.fasterxml.jackson.databind.util.ClassUtil#getConstructors
public static Ctor[] getConstructors(Class<?> cls) {
// Note: can NOT skip abstract classes as they may be used with mix-ins
// and for regular use shouldn't really matter.
if (cls.isInterface() || isObjectOrPrimitive(cls)) {
return NO_CTORS;
}
Constructor<?>[] rawCtors = cls.getDeclaredConstructors();
final int len = rawCtors.length;
Ctor[] result = new Ctor[len];
for (int i = 0; i < len; ++i) {
result[i] = new Ctor(rawCtors[i]);
}
return result;
}
看到getDeclaredConstructors()後我蒙了, 我斷點下用idea執行器試了下還真的獲取不到無參構造,
然後在項目中弄了個測試類去調PageResult.class的getDeclaredConstructors方法確實獲取不到
那爲啥升級前就行? 我回退了版本然後調測試類, 發現居然能獲取到一個私有的無參構造器.
然後我看了一下VO...上邊有一個註解@Data, 然後分別查看了兩個版本編譯後的class文件, 一個有私有無參構造, 一個沒有
問題原因
我升級了SpringBoot後lombok的版本也升級了, 從1.16.22升級到了1.18.12
新版本lombok的@Data註解不會自動生成無參構造函數, 而舊版本的會生成一個私有的無參構造函數.
思考
總以爲是框架問題, 但錯誤提示很清楚的說了是構造器找不到, 直接去對比下兩個版本編譯後的class文件就知道是lombok的問題.
我本以爲舊版jackson的會自動識別並使用有參構造器來初始化類(感覺比較智能), 分析源碼後發現人家從舊版到新版這部分類初始化的代碼就沒改過,哈哈哈 是我想太多了.