@@ -555,6 +555,20 @@ private <T> CachedConstructor<T> loadConstructorMetadata(Class<T> cls) {
555555 parameterIndexes .put (name , i );
556556 }
557557
558+ // Check for transitive context requirements: if any non-injection parameter type
559+ // itself requires context (e.g., nested objects with @MaxMindDbIpAddress annotations),
560+ // then this parent class also requires context to avoid incorrect caching.
561+ if (!requiresContext ) {
562+ for (int i = 0 ; i < parameterTypes .length ; i ++) {
563+ if (parameterInjections [i ] == ParameterInjection .NONE ) {
564+ if (shouldInstantiateFromContext (parameterTypes [i ])) {
565+ requiresContext = true ;
566+ break ;
567+ }
568+ }
569+ }
570+ }
571+
558572 var cachedConstructor = new CachedConstructor <>(
559573 constructor ,
560574 parameterTypes ,
@@ -602,8 +616,15 @@ private <T> Object decodeMapIntoObject(int size, Class<T> cls)
602616 parameters [i ] = injectParameter (parameterInjections [i ], parameterTypes [i ]);
603617 continue ;
604618 }
605- if (parameters [i ] == null && parameterDefaults [i ] != null ) {
619+ if (parameters [i ] != null ) {
620+ continue ;
621+ }
622+ if (parameterDefaults [i ] != null ) {
606623 parameters [i ] = parameterDefaults [i ];
624+ continue ;
625+ }
626+ if (shouldInstantiateFromContext (parameterTypes [i ])) {
627+ parameters [i ] = instantiateWithLookupContext (parameterTypes [i ]);
607628 }
608629 }
609630
@@ -629,6 +650,77 @@ private <T> Object decodeMapIntoObject(int size, Class<T> cls)
629650 }
630651 }
631652
653+ private boolean shouldInstantiateFromContext (Class <?> parameterType ) {
654+ if (parameterType == null
655+ || parameterType .isPrimitive ()
656+ || isSimpleType (parameterType )
657+ || Map .class .isAssignableFrom (parameterType )
658+ || List .class .isAssignableFrom (parameterType )) {
659+ return false ;
660+ }
661+ return requiresLookupContext (parameterType );
662+ }
663+
664+ private Object instantiateWithLookupContext (Class <?> parameterType ) {
665+ var metadata = loadConstructorMetadata (parameterType );
666+ if (metadata == null || !metadata .requiresLookupContext ()) {
667+ return null ;
668+ }
669+
670+ var ctor = metadata .constructor ();
671+ var types = metadata .parameterTypes ();
672+ var defaults = metadata .parameterDefaults ();
673+ var injections = metadata .parameterInjections ();
674+ var args = new Object [types .length ];
675+
676+ for (int i = 0 ; i < args .length ; i ++) {
677+ if (injections [i ] != ParameterInjection .NONE ) {
678+ args [i ] = injectParameter (injections [i ], types [i ]);
679+ } else if (defaults [i ] != null ) {
680+ args [i ] = defaults [i ];
681+ } else if (types [i ].isPrimitive ()) {
682+ args [i ] = primitiveDefault (types [i ]);
683+ } else {
684+ args [i ] = null ;
685+ }
686+ }
687+
688+ try {
689+ return ctor .newInstance (args );
690+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e ) {
691+ throw new DeserializationException (
692+ "Error creating object of type: " + parameterType .getName (), e );
693+ }
694+ }
695+
696+ private static Object primitiveDefault (Class <?> type ) {
697+ if (type .equals (Boolean .TYPE )) {
698+ return false ;
699+ }
700+ if (type .equals (Byte .TYPE )) {
701+ return (byte ) 0 ;
702+ }
703+ if (type .equals (Short .TYPE )) {
704+ return (short ) 0 ;
705+ }
706+ if (type .equals (Integer .TYPE )) {
707+ return 0 ;
708+ }
709+ if (type .equals (Long .TYPE )) {
710+ return 0L ;
711+ }
712+ if (type .equals (Float .TYPE )) {
713+ return 0.0f ;
714+ }
715+ if (type .equals (Double .TYPE )) {
716+ return 0.0d ;
717+ }
718+ if (type .equals (Character .TYPE )) {
719+ return '\0' ;
720+ }
721+ return null ;
722+ }
723+
632724 private Object injectParameter (ParameterInjection injection , Class <?> parameterType ) {
633725 return switch (injection ) {
634726 case IP_ADDRESS -> getLookupIpValue (parameterType );
0 commit comments