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:
Executable
+54
@@ -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
|
||||
Executable
+42
@@ -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
|
||||
Executable
+25
@@ -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
|
||||
Reference in New Issue
Block a user