Add core-di: constructor vs setter vs field injection and @Autowired candidate resolution on Boot 4.1

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JoVmf2fWvcpoXndcDSwRf7
This commit is contained in:
Claude
2026-09-24 04:59:40 +00:00
parent b0c39a8d4c
commit 9825efa081
87 changed files with 1945 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Reads the resolution order out of Spring's own bytecode instead of trusting the reference
# documentation's prose: which determine* helpers DefaultListableBeanFactory calls, in which order.
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p output target/bytecode
JAR=$(find ~/.m2/repository/org/springframework/spring-beans -name 'spring-beans-*.jar' ! -name '*sources*' | sort | tail -1)
unzip -o -q "$JAR" 'org/springframework/beans/factory/support/DefaultListableBeanFactory*' -d target/bytecode
{
echo "# DefaultListableBeanFactory.determineAutowireCandidate, read with javap"
echo "# jar: $(basename "$JAR")"
echo
javap -p -c -cp target/bytecode org.springframework.beans.factory.support.DefaultListableBeanFactory 2>&1 \
| grep -v 'Picked up' \
| awk '/protected java.lang.String determineAutowireCandidate\(/{f=1} f{print} /^$/{if(f) exit}' \
| grep -E 'determineAutowireCandidate|invoke|instanceof' | sed 's/^ *[0-9]*: //'
} > output/23-determine-autowire-candidate-bytecode.txt
cat output/23-determine-autowire-candidate-bytecode.txt
# --- the early-reference caches behind circular-reference support
JAR2=$(find ~/.m2/repository/org/springframework/spring-beans -name 'spring-beans-*.jar' ! -name '*sources*' | sort | tail -1)
unzip -o -q "$JAR2" 'org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.class' -d target/bytecode
{
echo "# DefaultSingletonBeanRegistry: the fields that hold early references, read with javap"
echo "# jar: $(basename "$JAR2")"
echo
javap -p -cp target/bytecode org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 2>&1 \
| grep -v 'Picked up' | grep -E ' (singletonObjects|earlySingletonObjects|singletonFactories|singletonsCurrentlyInCreation|allowCircularReferences)[;]?' || true
echo
echo "# DefaultListableBeanFactory / AbstractAutowireCapableBeanFactory: the allow-circular-references switch"
unzip -o -q "$JAR2" 'org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.class' -d target/bytecode
javap -p -cp target/bytecode org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 2>&1 \
| grep -v 'Picked up' | grep -i 'allowCircularReferences' || true
} > output/24-early-reference-caches.txt
cat output/24-early-reference-caches.txt
# --- is the old Spring-specific @Nullable deprecated in 7.0.9?
JAR3=$(find ~/.m2/repository/org/springframework/spring-core -name 'spring-core-7*.jar' ! -name '*sources*' | sort | tail -1)
unzip -o -q "$JAR3" 'org/springframework/lang/Nullable.class' -d target/bytecode
{
echo "# org.springframework.lang.Nullable in $(basename "$JAR3"), read with javap -v"
echo
javap -v -cp target/bytecode org.springframework.lang.Nullable 2>&1 | grep -v 'Picked up' | grep -E 'Deprecated|RuntimeVisibleAnnotations|Ljava/lang/Deprecated|forRemoval|since' || true
} > output/25-spring-nullable-deprecation.txt
cat output/25-spring-nullable-deprecation.txt
# --- does the Boot parent compile with -parameters?
PARENT=$(find ~/.m2/repository/org/springframework/boot/spring-boot-starter-parent -name '*.pom' | sort | tail -1)
{
echo "# $(basename "$PARENT"): the compiler flag that parameter-name matching depends on"
echo
grep -n -B2 -A2 '<parameters>' "$PARENT" || echo "(no <parameters> element found)"
} > output/26-boot-parent-parameters-flag.txt
cat output/26-boot-parent-parameters-flag.txt
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Starts the real Boot application with three different profiles and captures what a developer
# actually sees: the "APPLICATION FAILED TO START" report for a circular dependency, and the same
# application starting once spring.main.allow-circular-references is switched on.
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p output
JAR=$(ls target/core-di-*.jar | head -1)
# Drops JAVA_TOOL_OPTIONS noise, machine-specific paths, timestamps and PIDs so reruns diff cleanly.
strip() {
grep -v -E '^Picked up JAVA_TOOL_OPTIONS' \
| sed -E 's#jar:nested:[^ ]*/target/#jar:nested:target/#; s#^[0-9T:.+-]+ (ERROR|WARN|INFO) [0-9]+ --- \[core-di\] \[[^]]*\] #\1 --- #' \
|| true
}
{
echo "# Boot start-up failure for a constructor cycle: --spring.profiles.active=ctor-cycle"
echo
java -jar "$JAR" --spring.profiles.active=ctor-cycle --spring.main.banner-mode=off --logging.level.root=ERROR 2>&1 | strip || true
} > output/19-boot-failure-analysis-ctor-cycle.txt
{
echo "# Boot start-up failure for a field-injection cycle: --spring.profiles.active=field-cycle"
echo
java -jar "$JAR" --spring.profiles.active=field-cycle --spring.main.banner-mode=off --logging.level.root=ERROR 2>&1 | strip || true
} > output/20-boot-failure-analysis-field-cycle.txt
{
echo "# Same field cycle with --spring.main.allow-circular-references=true"
echo
java -jar "$JAR" --spring.profiles.active=field-cycle --spring.main.allow-circular-references=true \
--spring.main.banner-mode=off --logging.level.root=WARN 2>&1 | strip
} > output/21-boot-allow-circular-references.txt
{
echo "# @Lazy on one constructor parameter: --spring.profiles.active=lazy-cycle"
echo
java -jar "$JAR" --spring.profiles.active=lazy-cycle --spring.main.banner-mode=off --logging.level.root=WARN 2>&1 | strip
} > output/22-boot-lazy-cycle.txt
cat output/19-*.txt output/20-*.txt output/21-*.txt output/22-*.txt
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Regenerates every file under output/.
#
# ./scripts/run-all.sh
#
# Needs a JDK 25 and Maven 3.9. Transcripts 01-18 come out of the test suite, which is the point:
# the figures in the two articles are assertions that fail the build if they stop being true.
set -euo pipefail
cd "$(dirname "$0")/.."
echo "== test suite (transcripts 01-18)"
mvn -B test
echo "== package the runnable Boot app"
mvn -B -q -DskipTests package
echo "== Boot start-up failure reports (19-22)"
./scripts/capture-failure-analysis.sh
echo "== bytecode and pom facts (23-26)"
./scripts/capture-bytecode.sh
echo
echo "output:"
ls -1 output