49/60 = 82%, against a 76% pass mark. Your best score in sixteen papers, and
+3 on #15. The last page asked for 48+, and you got 49. Spring Security 100%.
Testing 73% → 89%. Finished in 51m 32s.
The margin is now three questions (46/60 is the lowest pass), up from one. So this page is about
the two sections still under the pass mark, Spring Core (71%) and Spring Boot (75%), and the four misses
that were already answered on this site.
Section sizes: 21/11/9/9/2/8 reproduces all six official percentages (15/21 = 71.4% → 71%,
10/11 = 90.9% → 91%, 8/9 = 88.9% → 89%, 6/8 = 75%) and sums to 60. It also puts each miss in its obvious section.
Spring Core was 21 of the 60 questions, more than a third of the paper, and it's the lowest
section. Data's 91% is one miss out of eleven, not a slide: last time it was six questions.
Spring Boot's 75% is two misses out of eight, one question short of the pass mark.
| Attempt #15 | Attempt #16 | Result | |
|---|---|---|---|
| Score | 46/60 (77%) | 49/60 (82%) | Best of sixteen |
| Misses | 14 | 11 | −3 |
| Questions with a false tick | 11 | 8 | −3 |
| Stopped short (no false tick) | 3 | 3 | Held |
| Left blank | 0 | 0 | Held |
| Margin over the pass mark | 1 question | 3 questions | +2 |
| Time | 52m 34s | 51m 32s | 78 minutes unused |
All three marks came from false ticks. The sweeping-claim drill on #15 targeted exactly that. Across the eleven questions you missed there were 26 false options, and you rejected 17 of them. Only one question, Q58, had two false ticks.
Same question, not just the same topic. These four marks need no new study. Q25 is the clearest case: you ticked the same false option there for the third time.
| Q | Topic | Already on this site | Then → now |
|---|---|---|---|
| Q25 | proceed() "at most once" | Review #5 Q21 · Review #13 Q51 | Same false tick, third time |
| Q22 | What SpEL # references | Paper #2 Q50 · Review #14 Q49, with six drills | "Environment properties" → "literal values" |
| Q4 | Condition on a property value | Review #1 · Review #12 Q11 | Ticked @ConditionalOnBean → rejected it, stopped short |
| Q6 | What HttpMessageConverter is for | MVC round 3 Q15 | Three false ticks → one ("content negotiation") |
Q22 is a different wrong option each time, which
means the reasoning isn't sticking. So don't reason it out on the day, just remember the bank's answer: "starting
with #" means #{…}, and the answer is Spring beans.
Q4 and Q6 are half-fixed. You now reject the distractors that cost you before. What's left is taking both true answers on Q4 and keeping "negotiation" apart from "conversion" on Q6.
#15 said: a sweeping claim falls to one counterexample. That rule worked, and it's where your three marks came from. But on #16 two sweeping claims were true, and Q29 shows the rule being used as a verdict instead of a test.
| Q | The sweeping statement | Counterexample? | So it is |
|---|---|---|---|
| Q29 | "@WebMvcTest's context contains only web components" | Name a @Service it loads. You can't, which is why you @MockBean them | True, but you picked it as the incorrect one |
| Q29 | "It is essential to use @ExtendWith(SpringExtension.class)" | @WebMvcTest is already meta-annotated with it | False, and this was the answer |
| Q55 | "The constructor is the only method called during creation" | No @PostConstruct, no initMethod in this code. None | True |
| Q25 | "proceed() must be called up to 1 time only" | A retry loop calls it three times | False, and you ticked it |
Q29 had two sweeping options side by side. Only one of them had a counterexample. A sweeping word tells you to go looking. It doesn't tell you what you'll find. If you look and come up empty, the statement can be true.
A limit on Q55: no false option was ticked, so you left out at least one of the three true ones. The export can't show which. The "only" option is the likeliest, given Q29, but that's a guess.
| Bucket | Marks | What it costs you |
|---|---|---|
| The four repeats | 4 | No new material. Q4, Q6, Q22, Q25, all with drills already on this site |
| Injection by bean name | 2 | Q40, Q58 — one rule: the bean name is the key |
| Test the sweeping claim, don't reject it | 2 | Q29, Q55 — the two-way drill below |
| Take the rest once the false ones are gone | 3 | Q4, Q30, Q55 — you rejected every false option, then stopped |
| Never tick a name you can't place | 1 | BeanFactoryApplicationContext — the third glued fake in two papers |
The rows overlap (Q4 is in two, Q55 in two), so don't add them up. Spring Core and Spring Boot are the only sections under 76%. Five Core marks sit in the first three rows. Aim for 52+ next time, which is 87%.
| Q | Topic | What went wrong | The answer |
|---|---|---|---|
| Q4 | Gate a class on a property value | Stopped short — 2 were true | @ConditionalOnProperty · @ConditionalOnExpression |
| Q6 | What HttpMessageConverter is for | + "responsible for content negotiation" | Content conversion in HTTP communication |
| Q21 | Health show-details + a group | + "details only for the group's checks" | Details for all users, no authentication needed |
| Q22 | What SpEL # references | + "literal values" | Spring beans |
| Q25 | AOP advice statements | + "proceed() at most once" | @Around first param is ProceedingJoinPoint · throwing filters |
| Q29 | The incorrect @WebMvcTest statement | Picked "contains only web components" (true) | "@ExtendWith(SpringExtension) is essential" |
| Q30 | Finder method ending keyword | Stopped short — all 4 were true | Is · LessThan · GreaterThan · Null |
| Q36 | ApplicationContext implementations | + BeanFactoryApplicationContext — invented | ClassPathXml… · FileSystemXml… |
| Q40 | @Autowired Map<String, T> | + "keys are canonical class names" | Keys are bean names |
| Q55 | Bean creation in a @Bean config | Stopped short — 3 were true | Constructor called · @PostConstruct works · constructor is the only call |
| Q58 | Three Foo beans — valid injections | + both ambiguous injection points | @Qualifier, or a field/parameter named like a bean |
Every statement comes from this paper, and about half of them are true. For each one, look for the counterexample. If you find one, answer False. If you look and there isn't one, answer True. Don't let the wording decide for you.
| Statement | The search | Found? | Verdict |
|---|---|---|---|
"proceed() at most once" | Any advice that calls it twice? | A retry loop | False |
"@WebMvcTest loads only web components" | Any service or repository it loads? | None | True |
"@ExtendWith(SpringExtension) is essential" | Does it work without it? | Yes, it's meta-annotated | False |
| "The constructor is the only method called" | Any init callback in this code? | None | True |
proceed() may be called any number of times — once, many times, or not at allproceed() must be called up to 1 time only", the same option
you ticked on #5 and #13. You correctly rejected @AfterFinally, which doesn't
exist.@Around("@annotation(Retryable)")
public Object retry(ProceedingJoinPoint pjp) throws Throwable {
for (int attempt = 1; ; attempt++) {
try {
return pjp.proceed(); // called up to 3 times
} catch (TransientException e) {
if (attempt == 3) throw e;
}
}
}
@Around("@annotation(Cached)")
public Object cache(ProceedingJoinPoint pjp) throws Throwable {
Object hit = cache.get(pjp.getArgs());
return hit != null ? hit : pjp.proceed(); // called zero times on a hit
}
| Option | Verdict | Why |
|---|---|---|
proceed() must be called up to 1 time only | False | Retry calls it many times; a cache hit calls it zero |
@AfterFinally runs however the method exits | False | No such advice. That's @After |
The first parameter of @Around must be ProceedingJoinPoint | True | It's how the advice calls the target |
@AfterThrowing(throwing = …) narrows matching to that exception type | True | The Spring reference: the throwing clause restricts matching to executions that throw that type |
proceed(): zero (cache), once (normal), many (retry). All legal. Around = ProceedingJoinPoint first.# references ticked a false option 3rd time · paper #2 & review #14#{…}, and its answer is Spring beans| You write | What it is | Which option it matches |
|---|---|---|
#{@orderService.limit} | SpEL, reaching into a bean | Spring beans — the answer |
${app.name} | Property placeholder, no # at all | Environment properties |
#{'hello'} · #{42} | Literals need no prefix; #{} adds nothing | Literal values |
#{systemProperties['java.version']} | Reached through a named object, not the # | JVM properties |
Why your reasoning keeps drifting: the stem is loose. #{42} is a legal
expression, so "literal values" sounds plausible. Inside an expression, #id is a
variable, like a method parameter in @Cacheable(key = "#id"). Review #14 covers both
meanings. For this stem, stop reasoning and use the answer the bank gave twice: beans.
#{@bean} → Spring beans. $ = properties. Literals need nothing.@Autowired Map<String, T> ticked a false option@Component("json") class JsonConverter implements MessageConverter { }
@Component("xml") class XmlConverter implements MessageConverter { }
@Autowired Map<String, MessageConverter> converters;
// { "json" -> JsonConverter@1a2b, "xml" -> XmlConverter@3c4d }
The test that settles it: define two @Bean methods returning the
same class. Keyed by class name, one would overwrite the other. Bean names are unique in a context, so
they're the only key that works.
| Injection point | Gets | Sorted by @Order |
|---|---|---|
List<T> · T[] | Every bean of type T | Yes |
Set<T> | Every bean of type T | No |
Map<String, T> | Bean name → bean, for every T | No |
T — one bean | Exactly one, or an exception | — |
@Primary only picks a winner for a single T. A collection or map still
gets every candidate.
Foo beans — which injections work ticked 2 false optionssetFoo(Foo foo) and private
Foo foo. The only question on this paper with two false ticks, and both fail for the
same reason.| Injection point | Name Spring tries | Matches a bean? | Result |
|---|---|---|---|
@Autowired @Qualifier("foo3") Foo foo; | — qualifier decides | foo3 | Valid |
@Autowired private Foo foo2; | foo2 | foo2 | Valid |
setFoo(@Qualifier("foo1") Foo foo) | — qualifier decides | foo1 | Valid |
setFoo(Foo foo2) | foo2 — the parameter | foo2 | Valid |
setFoo(Foo foo) | foo | No | NoUniqueBeanDefinitionException |
@Autowired private Foo foo; | foo | No | NoUniqueBeanDefinitionException |
The order Spring tries, with several candidates: @Qualifier narrows
the list → @Primary → highest @Priority → a field or parameter name that equals a bean
name → otherwise, fail. The name it matches is the field or parameter, never the setter's method name.
setFoo2(Foo foo) still fails.
Same rule as Q40: in both questions the bean name is what connects the injection point to the bean.
foo ≠ foo1/foo2/foo3, so it fails.ClassPathXmlApplicationContext and FileSystemXmlApplicationContext — BeanFactoryApplicationContext doesn't existBeanFactoryApplicationContext. You correctly
rejected YamlApplicationContext.The glued-name tell again: BeanFactory + ApplicationContext.
#15 had TransactionStatusAdvice and @AutoConfigureDataSource. That's three invented
names in two papers, each made from real parts.
| Real implementation | Loads from |
|---|---|
ClassPathXmlApplicationContext | XML on the classpath |
FileSystemXmlApplicationContext | XML on the file system |
AnnotationConfigApplicationContext | @Configuration classes |
GenericApplicationContext · GenericXmlApplicationContext | Flexible; register beans programmatically |
AnnotationConfigWebApplicationContext · XmlWebApplicationContext | Web apps |
AnnotationConfigServletWebServerApplicationContext | What Spring Boot creates for a servlet app |
A slip in the bank's explanation: it lists WebApplicationContext as an
implementation. It's an interface that extends ApplicationContext. The two web classes in
the table implement it.
@Bean method stopped shortinitialize()
is called automatically".| Option | Verdict | Why |
|---|---|---|
| The constructor is called when the container creates the bean | True | new MyBean() runs inside myBean() |
myBean() is called every time the bean is requested | False | Singleton; the @Configuration CGLIB proxy returns the cached instance |
initialize() is called automatically | False | Needs @PostConstruct or @Bean(initMethod = "initialize") |
Init logic can go in a @PostConstruct method | True | Runs after dependency injection |
| The constructor is the only method called during creation | True | Look for an init callback in this code: none |
The callback order, when there are several: constructor → dependency injection →
@PostConstruct → afterPropertiesSet() → initMethod. And note the
asymmetry: for @Bean, Spring infers a destroy method (a public close() or
shutdown()) but never infers an init method. A method named initialize() is just a
method.
@ConditionalOnProperty and @ConditionalOnExpression — two answers@ConditionalOnBean; this time you didn't.// direct: made for exactly this
@ConditionalOnProperty(name = "enable.class.mycustomautoconfig", havingValue = "true")
// SpEL: a placeholder inside an expression, evaluated as a boolean
@ConditionalOnExpression("${enable.class.mycustomautoconfig:false}")
| Condition | Tests |
|---|---|
@ConditionalOnProperty | A property's presence or value |
@ConditionalOnExpression | Any SpEL expression, which can read properties |
@ConditionalOnResource | A file or classpath resource exists |
@ConditionalOnBean | A bean exists in the context |
Two details: without havingValue, @ConditionalOnProperty
matches any value except false. And a missing property doesn't match unless you set
matchIfMissing = true.
${…}. Resource and Bean test something else.show-details: always + a health group ticked a false optionshow-details is set on the endpoint, so it covers everything; a group only adds a URLmanagement:
endpoint:
health:
show-details: always # applies to /actuator/health and every group
group:
custom:
include: diskSpace, ping # adds /actuator/health/custom
| URL | Shows | Details? |
|---|---|---|
/actuator/health | Every indicator: db, diskSpace, ping, … | Yes, for everyone |
/actuator/health/custom | diskSpace + ping | Yes — the group inherits show-details |
show-details values: never (default) ·
when-authorized · always. A slip in the bank's explanation: it says
when-authorized means "users with the ACTUATOR role". In fact it means any authenticated user, unless
you list roles in management.endpoint.health.roles. A group can override
show-details for itself, but this one doesn't.
show-details on the endpoint covers every group.HttpMessageConverter is for ticked a false option MVC round 3 · Q15| Step | Question it answers | Component |
|---|---|---|
| 1. Negotiate | Which media type? (reads Accept) | ContentNegotiationManager |
| 2. Convert | Turn this object into JSON bytes, or bytes into an object | HttpMessageConverter |
| — | Translate a message into French | MessageSource |
Why it was tempting: the bank's correct option also uses the word, "used to negotiate content conversion". The false one says the converter is responsible for content negotiation. Read which noun the sentence ends on: conversion is right, negotiation is wrong.
@RequestBody in, @ResponseBody out.@WebMvcTest picked a true statement@ExtendWith(SpringExtension.class) is essential" — it's already built in@WebMvcTest contains only web
components", which is true. You correctly left "class-level" and "a single controller"
alone.| Statement | True? | So is it the answer? |
|---|---|---|
With JUnit 5 you must add @ExtendWith(SpringExtension.class) | False — it's a meta-annotation of @WebMvcTest | Yes |
@WebMvcTest is a class-level annotation | True | No |
| It can target a single controller | True — @WebMvcTest(OrderController.class) | No |
| Its context contains only web components | True — that's what "slice" means | No |
@WebMvcTest(OrderController.class) // no @ExtendWith needed
class OrderControllerTest {
@Autowired MockMvc mvc;
@MockBean OrderService service; // not loaded by the slice — supply it
}
What the slice loads: @Controller, @ControllerAdvice,
@JsonComponent, Converter, Filter, WebMvcConfigurer,
HandlerMethodArgumentResolver, plus MockMvc. It doesn't load @Service,
@Repository or plain @Component beans. Every Boot test annotation
(@SpringBootTest, @DataJpaTest, @JsonTest…) already carries the extension.
SpringExtension. "Essential" is the false word here; "only web components" is the definition of a slice.Is, LessThan, GreaterThan, Null| Keyword (and alias) | Example | JPQL |
|---|---|---|
Is · Equals · or nothing | findByNameIs(name) | name = ?1 |
LessThan · IsLessThan | findByAgeLessThan(18) | age < ?1 |
GreaterThan · IsGreaterThan | findByAgeGreaterThan(65) | age > ?1 |
Null · IsNull | findByEmailNull() | email IS NULL |
Between · Like · StartingWith · Containing · In · True · Before | Also valid | |
| BiggerThan · Above · MoreThan | Not keywords | |
The pattern: most keywords have an Is form and a bare form, so
Null is as valid as IsNull. On a "which are valid" question where every option looks
like a real keyword, ticking all of them is allowed.
proceed() may be called zero, one or many times. @Around's first parameter is ProceedingJoinPoint; throwing filters by exception type#{@bean}). ${} is properties; literals need no prefixMap<String, T> = bean name → bean, for every bean of type T. @Primary doesn't filter itfoo matches no fooN bean, so it failsBeanFactoryApplicationContext is glued from real parts@Bean infers destroy, never init@ConditionalOnProperty or @ConditionalOnExpression("${…}"). Both are answersshow-details on the endpoint covers every group. Groups add /actuator/health/{name}; they hide nothingHttpMessageConverter converts; ContentNegotiationManager negotiates@WebMvcTest already includes SpringExtension and loads only web components. That is true, not a trapIs, LessThan, GreaterThan, Null are all finder keywords. Ticking every option is allowedEleven questions with no topic label to prime you. For each sweeping option, look for the counterexample and let the search decide. Several of these have more than two right answers.