Exam Review #3 · 17 Aug 2026 · 23 to fix

Third attempt — every miss, and the fact that fixes it

You scored 37 / 60 = 62%−3 marks on attempt #2. Pass is 76% (46/60), so you need +9 questions. Don't read the dip as going backwards: this paper drew from a different pool — the only topic that came back from attempt #2's 20 misses is @Order (and from a new angle). It exposed fresh gaps instead — AOP, REST clients, Boot packaging. Read each fix, then drill the quiz at the end of every section (options reshuffle on each load).

62%
This attempt (37/60)
67%
Attempt #2 (40/60)
65%
Attempt #1 (39/60)
+9
Questions to pass
Official result · practice exam

62% — you did not pass this time

76% required to pass · 60/60 answered · 37 correct · time used 1h 14m 34s of 2h 10m
Spring Core 68%6 misses Data Management 60%2 misses Spring MVC 60%4 misses Testing 83%1 miss Spring Security 33%2 misses Spring Boot 45%8 misses

How to read this. Testing (83%) is the only section above the pass line — everything else would sink you. The two red bars are different problems: Spring Security 33% is a small section (2 misses out of ~3 questions), so the percentage looks brutal but the fix is tiny — memorise the two facts in §6. Spring Boot 45% is the real damage: 8 raw marks, the most of any section, and it is a heavily weighted part of the blueprint. Spring Core at 68% is only "nearly there" because it's the biggest section — 6 raw misses hide inside it, half of them AOP. You also finished in 1h 15m of 2h 10m — nearly an hour unused. That's the single easiest change: slow down and re-read every multi-select stem.

Trend: 39 → 40 → 37. Flat, and that's the real signal. Three attempts, 64 distinct misses, almost no overlap — you are not failing the same questions, you are thin across the whole blueprint. The cure isn't more mocks; it's closing whole topics. This paper's biggest bleed by far is Spring Boot & Actuator (8 misses) — the section you last revised and the cheapest to fix.
Where you bled points this time Spring Boot & Actuator (8), MVC / REST (4), AOP (3), Spring Core (3), Data access (2), Security (2), Testing (1). Two clusters — Boot packaging/Actuator and AOP advice types — account for 11 of the 23. (The sections below split AOP out of Spring Core and REST out of Spring MVC, so the counts are finer grained than the official six-section report above; Core 3 + AOP 3 = the report's 6, MVC 2 + REST 2 = its 4.)
Read this before the fixes: three habits that cost you marks

Jump to a section

① Spring Boot & Actuator — 8 misses ② Spring MVC & REST — 4 misses ③ AOP — 3 misses ④ Spring Core — 3 misses ⑤ Data access — 2 misses ⑥ Spring Security — 2 misses ⑦ Testing — 1 miss

① Spring Boot & Actuator 8 misses

Q2 · @SpringBootApplication

It adds three things — and takes nothing away

@SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan. So the two correct statements are: it enables auto-configuration, and component scanning starts from the annotated class's package (and sub-packages). Everything else was an absolute distractor: @Bean methods in the class are not ignored (it is a @Configuration class), other annotations like @EnableScheduling are not ignored, and it does not create a separate ApplicationContext per annotated class — one context per application.

Memory hook: Config + AutoConfig + ComponentScan. It only ever adds — nothing gets "ignored".
Q3 · Embedded containers

Four supported: Tomcat, Jetty, Undertow, Netty

Tomcat (default, servlet), Jetty (lightweight servlet), Undertow (non-blocking servlet) and Netty — the one people forget, because it's the default for WebFlux (spring-boot-starter-webflux), not for servlet apps. Azure is a cloud platform, not a container. Swap containers by excluding spring-boot-starter-tomcat and adding the jetty/undertow starter.

Memory hook: T-J-U for servlet, Netty for reactive. Four containers, not three.
Q7 · Fat jar vs original jar

Fat jar = executable, bigger, nested deps, Main-Class + Start-Class

The spring-boot-maven-plugin repackages the plain jar. In the fat jar's META-INF/MANIFEST.MF: Main-Class: org.springframework.boot.loader.JarLauncher and Start-Class: com.example.MyApplication. Layout: BOOT-INF/classes/ (your code), BOOT-INF/lib/ (every runtime dependency as a nested jar) and the loader classes. So it's executable with java -jar, contains all dependencies, and is strictly larger — "smaller than the original" was the trap. Note it is not a shaded/uber jar (classes aren't merged), and because nested jars are invisible to the standard class loader you can't put a fat jar on someone else's -cp.

Memory hook: JarLauncher launches, Start-Class starts. BOOT-INF/lib = nested, so fat > thin, always.
Q26 · Actuator endpoint names

There is no /off — shutting down is /shutdown

Real endpoints: health, info, metrics, beans, env, configprops, mappings, loggers, threaddump, heapdump, conditions, scheduledtasks, shutdown. /off is invented. Remember the pairing with attempt #2's fact: every endpoint is enabled by default except shutdown, and only health (plus info) is exposed over HTTP by default.

Memory hook: shutdown, not off. Enabled ≠ exposed.
Q28 · Micrometer Gauge

A gauge polls a value you maintain — it never updates itself, and it isn't cumulative

Correct statements: AtomicInteger::get is thread-safe without locks; a custom gauge registered with the MeterRegistry is automatically exposed via /actuator/metrics; and a gauge is a snapshot of the current value — no aggregation or averaging. The two you must reject: gauges are not for cumulative totals (that's Counter), and the gauge does not "automatically update" — your code must change the source object (gaugeValue.incrementAndGet()); the gauge merely reads it when scraped.

Meter cheat-sheet: Gauge = current value (queue depth, active connections) · Counter = monotonically increasing count (requests, errors) · Timer = duration + count · DistributionSummary = distribution of values. Gotcha worth knowing: a gauge holds a weak reference to its source — if the object is GC'd the gauge reports NaN, which is why the source is declared as a @Bean.

Memory hook: Gauge reads, Counter counts. You update the value; the gauge only looks at it.
Q32 · Which endpoint lists all URLs?

mappings — the collated list of every @RequestMapping

mappings shows every request-mapped path in the app. Don't confuse: info = arbitrary app info · metrics = JVM/HTTP/custom metrics · httptrace = the last N request/response exchanges (and it's removed from Boot 3 in favour of httpexchanges) · beans = every bean in the context.

Memory hook: "All possible URLs" = mappings. "What happened on those URLs" = httptrace.
Q48 · Closing the ApplicationContext

Do nothing — Boot registers the JVM shutdown hook for you

SpringApplication.run() internally calls AbstractApplicationContext#registerShutdownHook(). On JVM exit (SIGTERM, System.exit()) the hook runs doClose()destroySingletons() + @PreDestroy callbacks + ContextClosedEvent. So no action is required. Calling close() or registerShutdownHook() yourself isn't wrong Java, but it's redundant here and isn't the answer. There is no "Auto-Close" property — invented. Opt out with app.setRegisterShutdownHook(false); graceful shutdown (server.shutdown=graceful, Boot 2.3+) is a separate mechanism that drains in-flight requests.

Memory hook: Boot already registered the hook. The answer to "how do I close it?" is "you don't".
Q53 · @PropertySource vs application.yaml

No — application.yaml/.properties are auto-loaded

Boot loads application.properties, application.yaml and application-{profile}.yml automatically from the standard locations — @PropertySource is never needed for them, wherever they sit on the classpath. @PropertySource is for extra, custom files (@PropertySource("classpath:external-service.properties")). The killer detail: @PropertySource does not support YAML at all — for a custom YAML file use spring.config.import (Boot 2.4+), a PropertySourceFactory, or @TestPropertySource in tests.

Memory hook: application.* = free. Custom file = @PropertySource — and @PropertySource can't read YAML.

② Spring MVC & REST 4 misses

Q14 · @RestController basics

In a @RestController, @ResponseBody is redundant — and @PathVariable is the Spring annotation

Two statements were true of that controller: GET is the right verb for a find (safe + idempotent; 201 Created is for creation), and @ResponseBody could be removed because @RestController = @Controller + @ResponseBody. The trap: @PathParam is JAX-RS, not Spring — Spring binds URI template variables with @PathVariable. (Same family of imposters: @QueryParam vs Spring's @RequestParam, @Path vs @RequestMapping.)

Memory hook: Anything ending in "…Param" from javax.ws.rs is JAX-RS bait. Spring = @PathVariable / @RequestParam.
Q17 · Binding the request body

@RequestBody — deserialises the payload via an HttpMessageConverter

Direction is everything: @RequestBody reads the incoming body into a parameter (JSON → object via MappingJackson2HttpMessageConverter, matching the Content-Type); @ResponseBody writes the return value to the response. @RequestParam = query string / form fields, @PathVariable = URI template segment. Combine with @Valid for bean validation. Pitfall: the body stream is read once, so you can't mix @RequestBody with form-data @RequestParam on the same handler.

Memory hook: RequestBody in, ResponseBody out. Param = ?query, Variable = /path/{seg}.
Q45 · RestTemplate — the INCORRECT statement

RestTemplate is synchronous (blocking)

True of RestTemplate: client-side, supports custom HttpMessageConverter registration, does automatic serialisation/deserialisation. False — and therefore the answer: "RestTemplate is asynchronous". It blocks until the response arrives. For async/reactive use WebClient. (The old AsyncRestTemplate existed but is deprecated.) RestTemplate itself has been in maintenance mode since Spring 5.

Memory hook: RestTemplate = sync + client-side + maintenance mode. Async ⇒ WebClient.
Q46 · WebClient

The non-blocking, reactive replacement for RestTemplate

Introduced in Spring 5 as part of WebFlux, built on Project Reactor (Mono/Flux). Its primary purpose is being a reactive HTTP client. The distractors all name real Spring features that belong elsewhere: WebSockets → WebSocketHandler; GraphQL → Spring for GraphQL. It can consume SSE, but that's a capability, not its purpose — watch for "primary purpose" in the stem.

Memory hook: WebClient = reactive HTTP client (Reactor, Mono/Flux). Works in servlet apps too.

③ AOP 3 misses

Q12 & Q13 · @After advice

@After is the finally block: always runs, sees nothing

@After runs after the join point whether it returned normally or threw, and it gets neither the return value nor the exception — so it's for cleanup, releasing locks/connections, audit logging "this finished". It cannot stop the target method executing and doesn't handle exceptions (they still propagate), and it doesn't replace or synchronise anything — the original method still runs, the advice just adds behaviour around it.

Learn this table cold — it generated two of your misses:

AdviceRunsReturn value?Exception?
@Beforebeforenono
@AfterReturningafter normal returnyes (returning=)no
@AfterThrowingafter an exceptionnoyes (throwing=)
@Afteralways (finally)nono
@Aroundbefore and afteryesyes (try/catch)
Memory hook: @After = finally: always runs, blind. Want the value → @AfterReturning. Want the exception → @AfterThrowing. Want control → @Around.
Q35 · @Before aspect — the SRP trap

All the mechanics were true; the design statement was the wrong one

True: @Aspect marks the class as an aspect; the pointcut execution(* com.example.service.*.*(..)) matches every method of every class in that package; the advice runs before each of them; and AOP applies it without modifying the service code. The false one: "if additional cross-cutting concerns arise, add them to LoggingAspect" — each concern (logging, security, auditing) belongs in its own aspect. Expect at least one question where the wrong option is a design-principle statement, not a technical one.

Pointcut reading practice: execution(* com.example.service.*.*(..)) — first * = any return type, service.* = any class directly in that package, .* = any method name, (..) = any arguments. Use service..* (two dots) to include sub-packages.

Memory hook: One aspect, one concern. And .* = that package only; .. = sub-packages too.

④ Spring Core 3 misses

Q11 · Registering bean definitions

@ComponentScan finds beans; @Component is what defines one

The exam's answer: bean definitions come from @Bean methods in a configuration class and from XML <bean> elements. @ComponentScan was marked incorrect because it only says where to look — the definition comes from the @Component stereotype on the scanned class. And new Foo() creates a plain object with no container lifecycle at all.

Full list of registration mechanisms for reference: @Bean methods · XML <bean> · @Component (+ scanning to discover it) · context.registerBeanDefinition(...) / BeanDefinitionRegistryPostProcessor · @Import of a config class or an ImportBeanDefinitionRegistrar.

Memory hook: @ComponentScan is a searchlight, not a factory. The @Component is the definition.
Q25 · @Order on @Configuration classes repeat topic

@Order sets processing order of configs — it never decides overriding or availability

With @Order(1) on FirstConfig and @Order(2) on SecondConfig, the exam's answer is: the configurations are processed in that order, FirstConfig first (lower value = higher priority). Everything else is false: @Order does not make later beans override earlier ones (that's bean-overriding rules / @Primary), does not express dependencies (@DependsOn), and does not restrict which beans exist — all beans are registered.

Reconcile this with attempt #2, where the answer was "@Order does not change bean instantiation order". Both are consistent — hold the distinction precisely: @Order ranks things in a sequence (config-class processing, elements of an injected List<T>, filter/interceptor chains) but never controls when the container instantiates a bean (@DependsOn, @Lazy) and never controls which bean wins (@Primary, @Qualifier).

Memory hook: @Order = position in a list. @DependsOn/@Lazy = when it's built. @Primary/@Qualifier = which one wins.
Q56 · Where @Value can be used

Field, constructor param, method, annotation type, and method params — if the method is an injection point

@Value is processed by AutowiredAnnotationBeanPostProcessor. Valid placements: on a field (most common) · on a constructor parameter · on a method · on an annotation type (it's in @Target, so you can build meta-annotations like @ServerPort) · on a method parameter when the method carries @Autowired. The one that fails: @Value on a parameter of an ordinary method with no @Autowired — Spring never calls that method, so nothing is injected.

Extra facts that show up: @Value does not work on static fields; an unresolvable placeholder fails startup with IllegalArgumentException: Could not resolve placeholder; defaults use @Value("${prop:default}") (empty default = ${prop:}); and injection happens before @PostConstruct.

Memory hook: A method parameter is only injected if the method itself is an injection point.

⑤ Data access 2 misses

Q36 · JdbcTemplate callbacks

Per-row and stateful = RowCallbackHandler

Match the three by the two axes in the stem — per-row? and stateful?:

CallbackPer row?Typically stateful?Returns
RowMapper<T>yesno (stateless)a T per row
RowCallbackHandleryesyesvoid — accumulates internally
ResultSetExtractor<T>no — whole ResultSetvariesone T

PreparedStatementCreator is the odd one out entirely — it creates the statement, it never touches the ResultSet.

Memory hook: Mapper returns per row (stateless) · Handler keeps state and returns void · Extractor swallows the whole ResultSet.
Q41 · DriverManagerDataSource

setUrl, setUsername, setPassword, setDriverClassName — there is no pooling

DriverManagerDataSource opens a brand-new connection on every getConnection(). So setPoolSize(...) doesn't exist — it's the distractor. It's for tests/dev only; in production Boot auto-configures HikariCP when spring-boot-starter-jdbc/-data-jpa is on the classpath.

Memory hook: DriverManagerDataSource = no pool, ever. Pooling is Hikari's job.

⑥ Spring Security 2 misses

Q54 · Built-in authentication mechanisms

Username/password and OAuth 2.0 are built in — MFA and OTP are not

Ships with Spring Security: Username/Password (form login, HTTP Basic, HTTP Digest), OAuth 2.0 / OpenID Connect, SAML 2.0, CAS, Remember-Me, JAAS, X.509, and pre-authentication (SiteMinder etc.). Not out of the box: MFA and OTP — both need custom extensions or third-party libraries. (Boot 2.5-era answer; treat "one-time token login" from Security 6.4 as out of scope.)

Memory hook: Password · OAuth2/OIDC · SAML · CAS · Remember-Me · JAAS · X.509 · pre-auth. No MFA, no OTP.
Q60 · UserDetailsService

One method — loadUserByUsername(String) — and any backing store you like

The interface is a single method: UserDetails loadUserByUsername(String username) throws UsernameNotFoundException. Not loadUser(...) — that name was the trap. Storage options are wide open: in-memory (InMemoryUserDetailsManager), JDBC/database (JdbcUserDetailsManager), LDAP (LdapUserDetailsService), or your own implementation — so "you can't store UserDetails in-memory" is plainly false. Expose a custom implementation as a @Bean and Spring Security picks it up.

The flow to recite: credentials submitted → AuthenticationManagerAuthenticationProvider (typically DaoAuthenticationProvider) → UserDetailsService.loadUserByUsername()PasswordEncoder.matches() → authenticated Authentication stored in the SecurityContext.

Memory hook: loadUserByUsername — the whole interface. Memory / JDBC / LDAP / custom.

⑦ Testing 1 miss

Q38 · @ContextConfiguration

It configures the ApplicationContext for integration tests — test layer only

@ContextConfiguration lives in org.springframework.test.context (spring-test) and tells the TestContext framework how to load and configure the ApplicationContext for a test: classes = AppConfig.class, locations = "classpath:test-context.xml", or initializers = .... It has nothing to do with production code — production contexts come from SpringApplication / @SpringBootApplication / AnnotationConfigApplicationContext.

Two facts worth banking: contexts are cached per unique configuration signature, so identical @ContextConfiguration across test classes reuses one context — and @DirtiesContext evicts that cache entry for every test sharing it, forcing a reload. In Boot, @SpringBootTest already loads the context (via SpringBootContextLoader), so adding @ContextConfiguration on top can conflict. @ContextHierarchy nests several of them into parent/child contexts.

Memory hook: @ContextConfiguration = test-only context recipe. Cached by signature; @DirtiesContext throws it away.

Your close-the-gap checklist — attempt #3

The 23 facts, one line each — cover this list from memory before the next go
Do this next — the plan that actually moves 62% → 76% Three attempts have produced 64 different misses, so drilling repeats won't be enough. Work topic-first: (1) re-read Lesson 15 · Spring Boot + Lesson 16 · Actuator — 8 misses here, the cheapest 8 marks on the table. (2) Re-read Lesson 7 · AOP and reproduce the five-advice table from memory. (3) Re-read Lesson 12 · REST for RestTemplate vs WebClient. (4) Then cover all three checklists — #1, #2 and this one — from memory, and only then take the Full Mock Exam again.
I'm your teacher — ask me anything. Want a full AOP advice-type drill, a Boot packaging + Actuator sweep, or a merged rapid-fire round over all 64 misses from the three attempts? Just ask. Say "quiz me on the 23" for this attempt only.
← Review #2 Retake the Mock Exam →