Constructor dependency ownership during ordinary class refresh
A direct refresh can reuse Spring's prepared constructor arguments without restoring the native dependency registration removed during destruction. This was measured on Spring 5.3.39, 6.1.14, and the installed SAP SDK Spring 5.3.43/6.2.12 graphs. The named cache repair invalidates only the selected owner's merged definition so native Spring resolves the current arguments and dependencies. Old edge names are never replayed and unrelated merged definitions are retained.
The additional ordinary refresh transaction covers one live plain zero-argument Helper feeding one plain constructor-injected leaf Owner, either in the same native factory or in one direct child of the Helper's root factory. Owner has one unchanged constructor taking that Helper, without parameter annotations. Before destroying Helper, the transaction captures both local singleton/definition identities and the native dependency relationship. It invalidates the Owner cache, reconstructs Helper then Owner, and records both old-to-new instance pairs for the existing reference healer. This recomputes constructor-captured values and updates the writable Owner reference in a held singleton caller. Arbitrary external objects, stacks and inaccessible/final fields are not a supported caller-retargeting mechanism.
Factory object identity and the local bean name identify the source. A child-local shadow or an unrelated root/sibling with the same name does not become a dependent of the changed source. Duplicate captures of one factory are processed once. The dependency plan is captured before native same-factory destruction can erase the owner, and a leaf already retired by native destruction is not destroyed twice. During a save batch, ordinary reconstruction waits until the batch completes; the single Helper snapshot subsumes a direct save of its leaf in either order.
Both mechanisms require an exact DefaultListableBeanFactory on one of the four measured versions, a standard directly constructing singleton definition, the exact live raw class, and compatible cached constructor metadata. BeanDefinition parent inheritance remains excluded. Parent-context enrollment is limited to the measured direct-child shape with a root parent; arbitrary parent chains and custom factories are not supported. Separate root contexts do not by themselves disable a local owner's named cache preparation. The existing root-only stable constructor-parameter qualifier behavior still uses native selection; changed constructor/parameter annotations reject cache preparation. This does not add qualified parent-child closure support.
The ordinary transaction further excludes aliases, multiple/transitive dependents, suppliers, factory methods/products, proxies/subclasses/interfaces, explicit constructor arguments, annotated injected state or methods, configured init/destroy methods and lifecycle-owned callbacks. Only plain unannotated, @Component or @Service classes meet its internal shape checks. A known affected unsupported closure or changed prepared identity is refused before retirement. Inapplicable creation policies continue through their existing direct/legacy route; bypassing ordinary enrollment is not a general guarantee that every old path refuses safely. Read-only preflight does not instantiate cold beans or FactoryBean products. Type lookup avoids eager FactoryBean initialization where the native API supports it.
Native construction can still fail after retirement. The restart ledger then records partial reconstruction; there is no singleton/business-work rollback or application-wide/in-flight request atomicity. No cross-save concurrency guarantee, general object-graph scheduler, callback migration or live tenant/cluster transition is added.
JMS, Kafka and Rabbit ownership checks precede ordinary enrollment. Their separately measured consumer transactions below remain distinct. The shared two-argument security/XML/Boot reconstruction seam is unchanged. Field-injected legacy cascades and scalar/property reconstruction are not enrolled in the new ordinary two-node transaction.
ConstructorDependencyContract covers native cache controls, direct refresh, current native candidate selection, local/parent isolation, shadow/sibling names, duplicate captures, unsupported closures, preparation revalidation, failed construction and both batch orders across the four framework graphs. The SDK constructor watcher fixture proves @Component body-save routing on 5.3.43/6.2.12 with stock SapMachine17/21 on macOS arm64. Unannotated XML/programmatic body-save discovery is not established by native component tests and is outside this increment. These are synthetic SDK-derived Spring graphs, not deployed SAP tenant acceptance. Full application/proxy/tenant variants and late attachment remain unverified.
JMS helper and constructor-property changes
For the measured Spring JMS 5.3.39 / ActiveMQ Classic 5.19.11 (javax.jms) and Spring JMS 6.1.14 / ActiveMQ Classic 6.1.3 (Jakarta Messaging) graphs, a plain local helper can feed a plain constructor-injected JMS listener. Saving only the helper class, or a property read by the helper's scalar @Value constructor, can recreate the listener with the current captured value. The listener source need not be saved again. When the same property also feeds the listener's own constructor, the successful helper transaction records the exact rebuilt names for that context; the later property sweep skips the already-current owner. This avoids a second destruction/duplicate endpoint and requires the valid save to be accepted, not merely to deliver a current message once. This is a runtime repair of the previously missing indirect consumer path.
Before native singleton destruction erases dependency records, Reclazz captures the actual direct dependent owners and their registered JMS callbacks. Every affected dependent must be a local plain leaf owner with one unchanged prepared constructor, a dependency only on that helper, and no further dependent beans. All selected consumers must finish destruction before a new owner is constructed. Native Spring recreates original callbacks; an existing Reclazz hidden added callback retains its saved metadata and supplier of the current named singleton. The completed transaction is excluded from the later generic bean cascade, so it is not destroyed a second time. Old dependency names are never re-registered.
Eligibility is deliberately narrow: one known root context, the native version-pinned bean factory, standard directly constructed definitions, and the standard JMS processor, registry, factory and message adapter. Callbacks require direct @JmsListener metadata with stable nonempty literal IDs and the native default or a literal container-factory name; generated IDs, repeatable/composed declarations and inherited callbacks are outside this transaction. The helper itself has no bean dependencies or listener callbacks. Watched fixtures use @Component helpers/listeners. Internal eligibility also accepts @Service and unannotated registered classes, but the unannotated native component tests do not establish automatic body-save discovery for such classes. Broader direct-listener stereotype support is a separate scope. Supplier/factory/proxy/custom definitions, explicit arguments, extra annotated state/advice, mixed-broker contexts, non-JMS or transitive dependents, same-name multiple/parent-child contexts, and inactive or already-retiring consumers require a restart before singleton destruction. A child's dependency on a parent helper is checked in the child's native factory. Unsupported helper bytecode may already have been reloaded; this is not bytecode rollback.
Indirect shutdown waits at most 30 seconds. Timeout retains the original helper/owner singleton identities and registry ownership while the worker may still be retiring. A later indirect save refuses an inactive retirement and requires restart; it does not start another concurrent shutdown. If creation or callback registration fails after destruction, this attempt's newly registered owned containers are cleaned up and the restart ledger records partial application. Unrelated consumers and merged bean definitions are retained. No reconstruction rollback or message-delivery rollback is promised. The property path may already expose its new Environment source and returns PARTIAL with the file pending if reconstruction fails. A direct constructor-property consumer that was not rebuilt by a coordinated helper transaction refuses before singleton destruction; this is a safety boundary, not direct-only listener-property reload support.
The permanent JmsConsumerCascadeContract compares fresh agentless values 10/20/30/40 with real watched helper/property saves and original/added callbacks, including coaffected helper/listener constructor values. It checks actual messages, current owner/constructor edges, unchanged listener source, retired workers before new-owner construction, bounded observed single delivery, unrelated delivery/identity, and context close. A native coaffected-value probe first reproduced a duplicate endpoint followed by APPLIED with stale messages; the coalescing prototype repaired it, and removing only its skip produced a false-PARTIAL acceptance failure. These native micro-controls are separate from file-watcher proof. Native component guards exercise direct-only property refusal, read-only preparation, creation-policy and parent-child refusals, changed-owner metadata, failed creation and a real blocked shutdown. These are scoped local broker observations on stock JDK17/21/macOS arm64 with startup -javaagent; child-loader delivery, helper-constructor annotation edits, multiple-owner save batches, external providers, XA and distributed delivery guarantees remain unverified.
Kafka helper and constructor-property changes
On Spring Framework 5.3.39 / Kafka 2.9.13 and Framework 6.1.14 / Kafka 3.2.4, a plain local helper and one directly constructor-injected leaf listener can follow helper-body or scalar constructor @Value property saves without saving the listener. Both nontransactional RECORD and the native default BATCH acknowledgment are measured with record-listener callbacks. BATCH acknowledgment does not mean batch-listener callback support. Original and already-added hidden callbacks use the current named owner; a property also feeding the owner's own scalar constructor rebuilds it once through the existing exact-name property coalescing.
Before native recursive destruction erases the dependency edges, Reclazz reads actual registered callback ownership, validates the whole bounded closure and snapshots its callback metadata. Every selected consumer must complete native stop(Runnable) before its registry entry is removed or a new owner is constructed. The stage1 per-owner constructor cache reset lets native reconstruction restore the current dependency edges. Native processing registers original callbacks; saved added callbacks replay through the application's existing processor against the current singleton. Unrelated containers keep their identity and delivery.
The transaction requires one known root context, one plain directly constructed helper with no bean dependencies, one local plain directly constructed leaf owner with no further dependents and one void callback with a stable literal ID. The standard pinned processor, registry, concurrent container, record adapter and factory are required. Both the live container and future factory template must have nontransactional RECORD or BATCH acknowledgment. Unsupported shapes refuse before consumer retirement/singleton destruction. This includes extra or custom processors, mixed-broker contexts, supplier/factory/proxy definitions, extra or transitive dependents, parent-child/multiple contexts, inactive consumers, transaction managers and other acknowledgment modes. Class bytecode or the Environment property source may already have changed; refusal is not rollback.
Actual registry ownership is inspected before strict processor eligibility. A native context with two typed Kafka processors and no listeners does not block unrelated bean or property refresh. A second typed processor added after startup to a real owned callback refuses its dependency refresh while keeping the old consumer and singleton identities and message delivery. This is a measured native case, not a promise about arbitrary broken/custom infrastructure.
Stop waits at most30 seconds. Timeout retains the helper/owner singletons and registry entries while a consumer may still retire; later indirect saves refuse and require restart. Failed reconstruction or registration after destruction records partial application and cleans up only the new owned callback. A direct-only constructor-property consumer outside a coordinated helper transaction refuses before singleton destruction. No application-work rollback, message rollback, transaction/rebalance/offset or exactly-once guarantee is provided. Security/XML/Boot callers of the two-argument reconstruction seam are unchanged.
KafkaConsumerCascadeContract runs12 watched cases per graph/JDK: original and added callbacks, helper body/property saves and coaffected own scalar, each with explicit RECORD or untouched native-default BATCH. Every case first verifies four fresh agentless startup values10/20/30/40, then three watched saves20/30/40. Assertions cover real messages, actual acknowledgment mode, unchanged listener source/direct-reload count, accepted saves, live singleton/dependency edges, captured native completion futures before replacement construction and at context close, unrelated identity/delivery and one observed delivery in a bounded window. Separate native guards cover23 lifecycle/refusal cases and3 optional-stack cases. These are stock JDK17/21/macOS arm64 startup-agent observations. Richer callback signatures/options, batch-listener APIs, multiple workers/partitions/owners, custom advice/conversion, other versions/providers/JVMs, child-loader delivery, concurrent configuration and distributed delivery remain unverified.
Rabbit helper and constructor-property changes
On Framework 5.3.39 / AMQP 2.4.17 and Framework 6.1.14 / AMQP 3.1.7, a plain directly constructed local helper and one directly constructor-injected plain leaf listener can follow helper-body or scalar constructor @Value saves without saving the listener source. The measured factory uses native default AUTO acknowledgment and nontransacted channels. Original callbacks and already-added hidden callbacks use the current named owner. A property also feeding the owner's own scalar constructor rebuilds it once through the existing exact-name property coalescing.
Before native recursive destruction removes dependency edges, Reclazz reads actual registered ownership, validates the bounded closure, snapshots the native single-method callback metadata and revalidates current identities. It retires only the selected consumer through the existing Rabbit worker coordinator. Native isRunning, isActive and consumer count are insufficient: a physical AMQP connection shutdown can report zero consumers while a callback still executes. Destruction and the tracked worker's complete run() exit, including channel cleanup, must finish before the registry ID is removed or a new owner is constructed. Stage1 constructor cache reset lets native reconstruction restore current dependency edges; Spring registers original callbacks and captured added metadata is replayed through native processAmqpListener. Unrelated containers retain identity and delivery.
This requires one known root context, one plain directly constructed helper with no bean dependencies, one plain local leaf owner depending only on that helper, one void callback with a stable literal ID, and the pinned standard processor/registry/simple factory/container/single-method adapter. Actual ownership is inspected before strict processor validation; the measured optional context with two Rabbit processors and no affected callbacks still permits unrelated class/property refresh. A real owned consumer with a second processor refuses before destruction. Known unsupported closures/configurations require restart before retirement: supplier/factory/proxy definitions, extra/transitive or parent-child/multiple-context dependents, multiple owners/callbacks, inactive consumers, mixed-broker contexts, custom executors/advice/batch containers, transaction managers, channel transactions and other acknowledgment modes. Original callback options outside id/queues/containerFactory/concurrency are not enrolled in this indirect transaction. Custom conversion/provider/configuration variants have no watcher acceptance from these fixtures.
The stop deadline is30 seconds. Incomplete shutdown preserves helper/owner singleton and registry ownership while a worker may still retire; later indirect saves refuse rather than start concurrent retirement. Failure after destruction can leave partial reconstruction: only the attempt's known new callback is cleaned up and restart is recorded. A direct-only constructor-property consumer outside a coordinated helper transaction refuses before singleton destruction. Class bytecode or Environment sources may already have changed; no application-work, property-source or message rollback is promised. The security/XML/Boot two-argument reconstruction seam and the committed JMS/Kafka transactions are unchanged.
RabbitConsumerCascadeContract supplies six real file-watcher cases per graph/JDK for helper body/property, original/added callbacks and coaffected owner scalar. Every case first verifies four fresh agentless native values10/20/30/40, then watches three saves20/30/40 with exact message values, accepted saves, live singleton/native edges, prior complete worker retirement before owner birth, unchanged listener source/direct reload count, unrelated identity/delivery and asserted full close. Separate24 native guards cover preflight/revalidation/refusals, creation failure before/after registration and real blocked shutdown with/without physical connection close; three optional-stack tests are component observations. These are stock SapMachine17/21/macOS arm64 startup-agent observations with pinned RabbitMQ4.3.5. Transactional/manual/batch acknowledgment, retry/recovery/distributed/exactly-once guarantees, child-context or child-loader delivery, transitive/multiple-owner or concurrent-save scope, custom advice/conversion/providers, late attachment and other versions/JVMs/OSs remain unverified. A bounded single-observed-delivery window is not a universal delivery guarantee.
Canonical source
This full page is generated from docs/spring-constructor-dependencies.md. Edit the repository source and rebuild; do not edit generated HTML.