升級SpringCloud到Hoxton.SR3後使用Fegin出現jackson反序列化失敗,源碼分析,原因lombok版本升級

關鍵詞

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的會自動識別並使用有參構造器來初始化類(感覺比較智能), 分析源碼後發現人家從舊版到新版這部分類初始化的代碼就沒改過,哈哈哈 是我想太多了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章