Revision for Exam Drill #21 · 5 Oct 2026 · 25 drill misses · 59 drills

The 25 drills you missed on the #21 page — and the six facts behind most of them

On the #21 review page you scored 41/66 (62%) across the eight drill blocks. No block was at full marks. That's a similar rate to the paper itself (68%), which means reading the explanations once didn't change the answers. This page takes a different angle on each fact: a small rule you can apply, a code sample you could run, and new questions that ask the same thing in a different way.

41/66
Drills right on #21's page
4/9
Lifecycle + hooks, the weakest block
3 / 4
@PostConstruct visibility questions missed
5
Proxy-vs-weaving misses

A limit on what I can see

The page you copied keeps the ✓ / ✗ for each question, but not which option you clicked, because the colours are lost in the copy. So this page knows which questions went wrong, and where a fact went wrong more than once, but not always how. Where I say what you probably ticked, I've inferred it from the paper or an earlier attempt, and I say so.
Fixed for next time: from now on every drill marks the options you chose with "← your pick" after you answer. That text survives a copy and paste, so the next revision can say exactly which option you picked.

Where the 25 came from

Block on #21's pageScoreWhat you missed
①c Lifecycle + hooks4/9Private @PostConstruct · constraints · non-web Environment · BFPP classes · where proxies are made
③ Testing3/6@MockBean (three true) · what @ContextConfiguration loads · the Feign fix
Mixed drill6/10Which modify bytecode · protected @PreDestroy · @Bean(initMethod, destroyMethod) · final class
⓪ The repeats5/8Non-web Environment · when BPP modifies · embedded server
①b DI and @Autowired8/12@Autowired's attributes · which cycles throw · name fallback · Boot 2.6 cycles
② Spring Boot4/6Tomcat timeout property · excludeName
①a Proxies, AOP, weaving7/10-javaagent for LTW · limits shared by both proxies · what a proxy does
④ Spring MVC4/5Which annotations bind a parameter

My drill progress on this page

Each finished block is recorded here in this browser. Reload to retake. Do the six fact blocks first, then retry all 25. Do the retry again tomorrow, without rereading. If you get it right a day later, the fact has stuck.

Drill blockQsLastBestRunsLast done
Loading…

What didn't stick, in order of cost

1 · Lifecycle methods: any visibility — 4 misses 2 · Proxy ≠ weaving — 5 misses 3 · DI resolution and cycles — 4 misses 4 · BFPP vs BPP — 3 misses 5 · Environment: non-web has two sources — 2 misses 6 · Boot config + test config — 6 misses Retry all 25, as they were

1 · Lifecycle methods can be any visibility 4 misses · also #7, #18, #21 on paper

Missed: @PostConstruct private void init() · constraints (with "not static") · @PreDestroy protected · @Bean(initMethod, destroyMethod)

This is now the most-missed fact on the whole site

You got the two-option version right in the repeats block, then missed three other questions that test the same rule. So the wording "must be public" is now familiar, but the rule behind it isn't. Here's the rule, shown in code instead of in a table:

@Component
class Probe {
    @PostConstruct
    private void init() { System.out.println("init ran"); }     // runs

    @PreDestroy
    protected void bye()  { System.out.println("bye ran"); }    // runs
}

Both lines print. Spring finds these methods by reflection (the CommonAnnotationBeanPostProcessor), calls ReflectionUtils.makeAccessible(method), then method.invoke(bean). makeAccessible switches off the visibility check, so private, protected, package-private and public all work the same.

Ask yourselfAnswerSo the rule is
What would Spring pass as an argument?Nothing, it has nothing to giveNo parameters
Who reads the return value?Nobodyvoid
Which object is it called on?This bean instanceNot static
Does reflection care about private?Not after makeAccessibleAny visibility

The same applies to @Bean(initMethod = "start", destroyMethod = "stop"). It's a third way to get the same two callbacks: start() runs after DI (after @PostConstruct and afterPropertiesSet()), and stop() runs when the context closes. It has nothing to do with scanning, and it doesn't run around every method call.

Memory hook: "No args, no return, not static, any door." Spring has a key to every door (reflection), so private doesn't stop it.

2 · A proxy is a second object; weaving edits the class 5 misses

Missed: what LTW needs · limits shared by both proxies · what a proxy does · which modify bytecode · final class

Two questions settle every AOP mechanics question

QuestionSpring AOP (JDK or CGLIB proxy)AspectJ weaving (compile-time or LTW)
1. Does the original class change?No. A second object is put in front of itYes. Its bytecode is changed
2. Does this.other() get advised?No. this is the target, not the proxyYes. The advice is in the method body itself
Needs a -javaagent?NoLoad-time, in a plain JVM: yes (spring-instrument.jar + META-INF/aop.xml). Compile-time: no, uses ajc
Anything on disk changed?NoCompile-time: yes. Load-time: no, only in memory

Then, for proxies only, which kind? Picture what each one has to be in Java:

// JDK proxy: a new class that IMPLEMENTS your interfaces
class $Proxy12 implements Payer { ... }               // needs an interface

// CGLIB proxy: a new class that EXTENDS your class
class Billing$$EnhancerBySpringCGLIB$$1a2b extends Billing { ... } // can't if Billing is final
                                                      // can't override final methods

The JDK proxy needs an interface. CGLIB is blocked by final. Only two limits hit both: self-invocation, and private methods (neither kind of proxy can override them). So a public final class Billing with no interface can't be proxied either way, and startup fails. It isn't silently skipped, because there's no proxy to skip the advice on.

Memory hook: Proxy = new object, class unchanged. Weaving = class changed. JDK implements, CGLIB extends. Both miss this. calls and private methods.

3 · How @Autowired picks a bean, and which cycles fail 4 misses

Missed: @Autowired's attributes · name fallback · which cycles throw · Boot 2.6 cycles

Treat it as a flowchart, not a list of facts

StepSpring asksIf yes
1How many beans of this type?0 → NoSuchBeanDefinitionException (or null if required = false). 1 → inject it
2Several. Is there a @Qualifier?Narrow to it
3Is exactly one @Primary?Inject it
4Does a bean's name match the field / parameter name?Inject it
5Still more than oneNoUniqueBeanDefinitionException

@Autowired takes part with one attribute, required. Narrowing by name is the job of @Qualifier("x") (or step 4). There's no name, value or qualifier attribute on @Autowired.

Cycles: ask "does an instance exist yet?"

CycleWhen A asks for B, does an A object exist?Plain SpringBoot 2.6+ default
Singletons, setter/fieldYes, it's constructed, just not filled inResolved (early reference)Fails
Singletons, constructorNo, A's constructor is still waitingThrowsThrows
PrototypesNo cache for half-built prototypesThrowsThrows

Boot 2.6 turned the setter case off by default: spring.main.allow-circular-references=false. @Lazy on one of the constructor parameters breaks any cycle, because Spring injects a lazy proxy instead of the real bean.

Memory hook: Type → qualifier → primary → name → fail. A cycle only resolves if a half-built singleton already exists, and Boot 2.6+ refuses even that.

4 · BFPP works on plans, BPP works on objects 3 misses · 3rd paper on BPP timing

Missed: when BPP modifies beans · which classes are BFPPs · where AOP proxies are created

Look at the method signatures and the answer follows

interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory f); // gets the PLANS
}
interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String name); // gets a REAL OBJECT
    Object postProcessAfterInitialization(Object bean, String name);  // and can return a replacement
}

Recognising them by name: Spring's names say what they are. …Configurer classes that edit definitions (PropertySourcesPlaceholderConfigurer, PropertyOverrideConfigurer) are BFPPs. Classes named …BeanPostProcessor (AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor) are BPPs, which is how they can set fields and call @PostConstruct on real objects.

Memory hook: BFPP gets the plans. BPP gets the object and can hand back a different one, which is where proxies come from.

5 · A non-web Environment has exactly two property sources 2 misses · both non-web

Missed: the non-web question in the repeats block and in the lifecycle block

You got the web version right both times, and the non-web version wrong both times

On #14 you gave a non-web app JNDI and the servlet parameters. These two misses suggest that hasn't changed, probably on JNDI. This is an inference: the copy doesn't show what you clicked. Use the application server as the test:

SourceWhere it comes fromExists in a plain java -jar / main() app?
systemPropertiesThe JVM (-Dx=y)Yes — every JVM has them
systemEnvironmentThe OS (export X=y)Yes — every process has them
servletContextInitParamsThe servlet containerNo container
servletConfigInitParamsThe servlet containerNo container
jndiPropertiesjava:comp/env, provided by the app serverNo app server

StandardEnvironment (non-web) adds the first two. StandardServletEnvironment (web) adds the other three on top. Profiles are available in both, because they're part of the Environment interface itself.

Memory hook: Non-web = JVM + OS. Everything else needs a server to provide it: servlet params and JNDI.

6 · Boot and test configuration: name the real thing 6 misses

Missed: embedded server · Tomcat timeout · excludeName · @MockBean · @ContextConfiguration · the Feign fix · parameter binding

Seven facts, each with the wrong answer it's easily confused with

NeedRealTrapWhy the trap fails
Different serverExclude Tomcat starter + add Jetty/Undertow starter"…for an external server"Every starter is embedded. External = WAR + SpringBootServletInitializer
Timeoutserver.tomcat.connection-timeoutserver.timeout, server.connection-timeoutOne doesn't exist; the other was removed in 2.3. Timeouts are per server
Exclude a class that's not on the classpathexcludeName = "a.b.FooAutoConfiguration"exclude = Foo.classA class literal for a missing class won't compile. A string always does
Extra auto-config in a slice@ImportAutoConfiguration(X.class)@ContextConfiguration(classes = X.class)@ContextConfiguration loads plain config classes / XML and ignores auto-config conditions and ordering
Avoid loading the whole appKeep the slice + @ImportAutoConfigurationSwitch to @SpringBootTestWorks, but it's the opposite of a slice test, so it isn't the best fix
@MockBeanClass or field · adds to the context · defines a new bean if none exists"Same as @Mock"@Mock has no Spring context. Three of the four options are true. You've stopped at two three times
Bind a parameter@RequestHeader, @CookieValue, @ModelAttribute, @RequestParam, @PathVariable, @RequestBody@ResponseBody, @RequestMappingThose two go on a method or class: one writes the response, the other maps the URL

@ModelAttribute is the easiest one to doubt. On a parameter it builds an object from form or query fields, as in save(@ModelAttribute Order order). That's binding.

Memory hook: Embedded ≠ external. Strings outlive missing classes. Auto-config goes in through @ImportAutoConfiguration. @MockBean = replace or add.

Retry all 25, as they were

These are the exact 25 drills you missed on the #21 page, reshuffled. Do this block after the six above, and again tomorrow without rereading. 25/25 tomorrow means the facts have stuck.

Next step. Do the retry block tomorrow, then copy the page again. Each option you chose will now be marked "← your pick", so I'll see exactly what you clicked. Or say "build the Spring Core clinic". Groups 1–4 on this page are all Core, and Core was half of the last paper.
← Dashboard ← Review #21 Boot clinic Testing clinic