Revision 2 for Exam Drill #21 · 7 Oct 2026 · 25 misses · 37 new drills + 25 retries

Revision 2 — you fixed "must be public". Circular dependencies are the wrong way round

On revision 1 you scored 34/59 (58%). This time every option you clicked was recorded, so this page shows exactly what you picked and why it was wrong. Nothing here is guessed.

0
"Must be public" ticks — fixed
19/25
Retry of the exact questions
15/34
New questions on the same facts
5
Misses from one wrong belief about cycles

What's fixed: @PostConstruct visibility

It took three exam papers, and this time it held. You picked all four visibilities for @PreDestroy, "runs after DI" for a private @PostConstruct, "is called" for a protected @PreDestroy, and you left "must be public" unticked in the constraints question. You also got the BPP phase, where AOP proxies are made, the LTW agent, which modes change bytecode, the embedded server question and @ModelAttribute right. Several of those were misses last time.

The gap between 76% and 44% is the most important number here

On the exact questions you'd seen before, you scored 19/25 (76%). On new questions testing the same facts, 15/34 (44%). Five times, you got a question right in its familiar wording and missed the same fact asked a different way:

FactFamiliar wordingNew wording
Non-web Environment sources✓ twice (retry 22, 24)✗ ticked servlet context params, twice
@MockBean adds a bean if none exists✓ (retry 2)✗ left "adds one" out — 4th time
What @ContextConfiguration loads✓ (retry 18)✗ "every auto-config in spring.factories"
Which classes are BFPPs✓ the Configurers (retry 3)✗ ticked the Configurers as BPPs
Excluding a class that's not on the classpath✓ excludeName (retry 21)✗ ticked exclude = Foo.class + scan filter

So you're recognising questions, not yet applying the rule. The real exam rewords questions, so the 44% is the better guide. That's why this page is mostly new questions, and why the retry block comes last.

My drill progress on this page

Each finished block is recorded in this browser. Reload to retake. Do blocks A–F first. Before you tick, say the rule to yourself (each section ends with one), then check every option against it.

Drill blockQsLastBestRunsLast done
Loading…

All 25, by what you picked

A · Circular dependencies, the wrong way round — 5 misses + @Autowired attrs (1) B · Environment: servlet params leaking into non-web — 4 misses C · @ContextConfiguration as the auto-config door — 3 + @MockBean (1) D · Final class vs final method — 3 misses E · BPP vs BFPP, read the full name — 3 + lifecycle signatures (2) F · Timeout property + excluding a missing class — 3 misses Retry the 25, exact wording

A · Circular dependencies 5 misses · one belief, the wrong way round

Your picks, across five questions

You think constructor cycles start up. They're the one kind that never does

QuestionYou pickedRight answer
Plain Spring: which cycles start up?field + setter + constructorfield + setter only
Plain Spring: which throw BeanCurrentlyInCreationException?setter + fieldconstructor + prototypes
Boot 2.7 field cycle fails. Fixes?switch to constructor injection + refactorrefactor + @Lazy + allow-circular-references=true
Boot 2.6+ setter cycle, by default?"resolves as in plain Spring"startup fails
@Lazy on a constructor parameter?"nothing — only works on classes"injects a lazy proxy, which breaks the cycle

Where the belief probably comes from: "constructor injection is recommended" is true, and it's easy to slide from there to "constructor injection fixes cycles". It's the reverse. Constructor injection is recommended partly because it makes a cycle fail at startup, so you notice the design problem and fix it.

Walk through it. Two beans, A needs B and B needs A:

// CONSTRUCTOR cycle: A(B b), B(A a)
create A → call new A(?) → needs a B first
  create B → call new B(?) → needs an A first
    is there an A? No: new A(...) hasn't returned, so no A object exists
    → BeanCurrentlyInCreationException                               ✗ always

// SETTER / FIELD cycle: A has @Autowired B b; B has @Autowired A a
create A → new A()                  → A exists, fields empty
           put an "early reference" to A in the singletonFactories cache
         → inject A.b → needs a B
  create B → new B()                → B exists
           → inject B.a → needs an A → found in the cache (the early reference)
  B finished
A.b = B → A finished                                                 ✓ plain Spring
                                                                     ✗ Boot 2.6+ by default

The only question that matters: when the second bean asks for the first, has the first one's constructor finished? With setter/field injection, yes, so Spring can hand over an unfinished object. With a constructor, no, so there's nothing to hand over. Prototypes fail too, because Spring never caches unfinished prototypes.

Plain Spring 5Boot 2.6+ defaultBoot with spring.main.allow-circular-references=true
Setter / field, singletonsStartsFails ("…form a cycle")Starts
Constructor, singletonsFailsFailsStill fails — the property can't help
PrototypesFailsFailsFails

@Lazy works on an injection point too, not only a class. Its targets are type, method, constructor, parameter and field. On an injection point, Spring injects a proxy straight away and only looks up the real bean when it's first used. Nothing waits for the other bean, so the cycle is broken, even a constructor one.

@Autowired attributes (retry 11): you picked "required and qualifier". If @Autowired had a qualifier attribute, you'd never write @Autowired @Qualifier("x") as two annotations. The fact that it takes two annotations shows that @Autowired only has required.

Rule to say before ticking: "Has the first one's constructor finished? Setter: yes → resolves (not in Boot 2.6+). Constructor: no → always fails. Constructor injection exposes cycles; it doesn't fix them."

B · Environment 4 misses · 0/4 on the block

Your picks

Two beliefs: servlet params exist without a servlet container, and JNDI can't be reached at all

QuestionYou pickedRight answer
Plain AnnotationConfigApplicationContext: sources?env vars + system props + servlet context paramsenv vars + system props
Non-web app can read…?all four, incl. web.xml context-paramenv var, profiles, -D flag
Which class adds jndiProperties?"neither — JNDI isn't reachable"StandardServletEnvironment
Web environment has…?jndi + servletConfig + systemProperties (left out systemEnvironment)all four

A correction to revision 1: that page guessed you were giving JNDI to non-web apps. Your picks show the opposite. You never ticked JNDI for non-web. The leak is servlet context parameters, and on JNDI you went to "not reachable at all".

Picture what env.getPropertySources() would print:

// plain main(): new AnnotationConfigApplicationContext(AppConfig.class)  → StandardEnvironment
[systemProperties, systemEnvironment]

// web app → StandardServletEnvironment = the same two, with three added in front
[servletConfigInitParams, servletContextInitParams, jndiProperties, systemProperties, systemEnvironment]
Rule to say before ticking: "Every app has 2 (JVM + OS). Web has 2 + 3 more (servlet config, servlet context, JNDI). 'Servlet' in the name means web only."

C · Getting auto-configuration into a test 3 misses + @MockBean

Your picks

You're using @ContextConfiguration to bring in auto-configuration

QuestionYou pickedRight answer
What does @ContextConfiguration(classes = TestConfig.class) do?"loads every auto-config in spring.factories"loads TestConfig as plain config
@DataJpaTest needs a library's auto-config@ContextConfiguration(classes = …AutoConfiguration.class)@ImportAutoConfiguration(…)
@WebMvcTest needs FeignAutoConfiguration (retry 10)same: @ContextConfiguration@ImportAutoConfiguration(…)
@MockBean: which are true?registers a mock + field/class (left out "adds one if none exists")all three

Look at the names. Auto-configuration only comes in through an annotation with AutoConfiguration in its name, or one that contains it:

AnnotationWhat it doesBrings in auto-config?
@EnableAutoConfigurationAll auto-config candidatesYes
@SpringBootApplicationIncludes @EnableAutoConfigurationYes
@ImportAutoConfiguration(X.class)Just the ones you name, as auto-config (ordering applied)Yes
@ContextConfiguration(classes = X.class)spring-test's loader. The test's version of new AnnotationConfigApplicationContext(X.class)No — X becomes plain config

@ContextConfiguration predates Boot. It's in spring-test, which knows nothing about spring.factories. So it can't load "every auto-config", and it isn't the way to add one.

@MockBean, for the fourth time: the name ends in Bean, so it puts a bean into the context. If a bean of that type exists, the mock replaces it. If none exists, it adds one. Both are true.

Rule to say before ticking: "No 'AutoConfiguration' in the name means no auto-config. @ContextConfiguration = plain classes you list. @MockBean = replace or add."

D · Final class fails loudly. Final method fails silently 3 misses · "silently skipped" twice

Your picks

You've merged the final-class and final-method cases

QuestionYou pickedRight answer
final class Report, no interfaces, @Transactional"created without a proxy; transactions silently skipped"startup fails
final class Billing, no interfaces (retry 14)same: "silently skipped"startup fails
After a CGLIB proxy is made for OrderServicetwo objects + "target's bytecode has advice inserted"two objects + OrderService.class unchanged
What's finalCan a proxy be made?Result
The class, no interfaceNo — CGLIB can't extend it, JDK has no interfaceLoud: startup fails, AopConfigException
The class, with an interface (JDK proxy)Yes — JDK never extendsWorks, through the interface
One method, class not finalYesSilent: the proxy can't override that method, so it goes straight to the target without advice

"Silently skipped" needs a proxy to exist and let one method slip past it. A final class with no interface never gets a proxy, and Spring won't hand out an unadvised bean when advice was required. It stops.

Bytecode: CGLIB does generate new bytecode, but for a new subclass (OrderService$$EnhancerBySpringCGLIB$$…). OrderService's own bytecode is untouched. Advice inserted into the target's own bytecode is weaving.

Rule to say before ticking: "Final class → no proxy → loud failure. Final method → proxy exists, that method slips past → silent. A proxy's bytecode is a new class; the target is unchanged."

E · BPP vs BFPP, and lifecycle signatures 3 + 2 misses

Your picks

Some of these look like misreading the two acronyms rather than not knowing

QuestionYou pickedRight answer
Which are BeanPostProcessors?the two ConfigurersAutowired… / CommonAnnotationBeanPostProcessor
Which hooks receive the bean instance?after-init + postProcessBeanFactorybefore-init + after-init
Order for one singletonDI → constructor → BPP after-initconstructor → DI → BPP before → @PostConstruct → BPP after
Which follow the @PostConstruct rules?all four, including staticthe three non-static ones
Which signatures are invalid?returns String + static (left out init(ApplicationContext ctx))all three of those

In the same session, the retry block asked "which are BFPPs?" and you picked the Configurers correctly. Then "which are BPPs?" got the same two. So the facts are there; you're mixing up the two acronyms while reading. Read the full names, not the acronyms. The answer is in the name:

Lifecycle signatures. Visibility is fixed. The two you missed are the other rules:

Rule to say before ticking: "Factory = definitions, before beans. PostProcessor = an object, around init. Lifecycle method: void name(), empty brackets, no static, any visibility."

F · Boot properties and exclusions 3 misses

Your picks

You haven't ticked server.tomcat.connection-timeout on either try

QuestionYou pickedRight answer
Which properties exist in Boot 2.5?server.connection-timeout + server.timeoutserver.tomcat.connection-timeout + server.jetty.connection-idle-timeout
Tomcat connection timeout (retry 17)spring.tomcat.timeoutserver.tomcat.connection-timeout
Exclude a class whose jar is missingexclude = Foo.class + @ComponentScan filterexcludeName = "…" + spring.autoconfigure.exclude

Build the property name from two rules:

  1. Embedded-server settings start with server., never spring. (server.port, server.servlet.context-path, server.ssl.*).
  2. A timeout works differently in each server, so the server's name comes next: server.tomcat.connection-timeout, server.jetty.connection-idle-timeout. The old generic server.connection-timeout was removed in 2.3.

Excluding a missing class: anything that takes a class literal (exclude = Foo.class, classes = Foo.class) won't compile when Foo's jar isn't there. So only the string forms work: excludeName = "com.acme.Foo" and spring.autoconfigure.exclude=com.acme.Foo. Scan filters never apply to auto-config at all.

Rule to say before ticking: "server.<which server>.<setting>. Missing class → strings only."

Retry the 25, exact wording

These are the 25 questions you missed on revision 1, reshuffled. Do them last. Because the wording is familiar, a high score here proves less than a high score on blocks A–F.

Next step. Do blocks A–F tomorrow too, without rereading, then copy the page. If A (cycles) and B (Environment) are at full marks a day later, those facts have stuck. Or say "build the Spring Core clinic". Five of the six groups on this page are Core.
← Dashboard ← Revision 1 Review #21 Testing clinic