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).
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.
@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.
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.
Main-Class + Start-ClassThe 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.
/off — shutting down is /shutdownReal 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.
GaugeCorrect 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.
mappings — the collated list of every @RequestMappingmappings 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.
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.
application.yaml/.properties are auto-loadedBoot 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.
@RestController, @ResponseBody is redundant — and @PathVariable is the Spring annotationTwo 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.)
@RequestBody — deserialises the payload via an HttpMessageConverterDirection 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.
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.
RestTemplateIntroduced 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.
@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:
| Advice | Runs | Return value? | Exception? |
|---|---|---|---|
@Before | before | no | no |
@AfterReturning | after normal return | yes (returning=) | no |
@AfterThrowing | after an exception | no | yes (throwing=) |
@After | always (finally) | no | no |
@Around | before and after | yes | yes (try/catch) |
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.
.* = that package only; .. = sub-packages too.@ComponentScan finds beans; @Component is what defines oneThe 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.
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).
@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.
RowCallbackHandlerMatch the three by the two axes in the stem — per-row? and stateful?:
| Callback | Per row? | Typically stateful? | Returns |
|---|---|---|---|
RowMapper<T> | yes | no (stateless) | a T per row |
RowCallbackHandler | yes | yes | void — accumulates internally |
ResultSetExtractor<T> | no — whole ResultSet | varies | one T |
PreparedStatementCreator is the odd one out entirely — it creates the
statement, it never touches the ResultSet.
setUrl, setUsername, setPassword, setDriverClassName — there is no poolingDriverManagerDataSource 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.
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.)
loadUserByUsername(String) — and any backing store you likeThe 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 →
AuthenticationManager → AuthenticationProvider (typically
DaoAuthenticationProvider) → UserDetailsService.loadUserByUsername() →
PasswordEncoder.matches() → authenticated Authentication stored in the
SecurityContext.
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.
service.* = that package, service..* = sub-packagesloadUserByUsername; in-memory / JDBC / LDAP / custom @Bean