46/60 = 77%, against a 76% pass mark. Your best score in fifteen papers (the
previous best was 73% on #9) and +8 marks on #14 — exactly the gap the last page said you had to
close. Data Management 100%. Testing 33% → 73%. Finished in 52m 34s.
The margin is one question: 45/60 is 75%, which fails. So this page is about making one spare
mark into three or four. Twelve of these fourteen misses cover material already on this site, and eight are
the same question you missed before.
The section sizes are exact, not estimated. Fourteen misses spread over 20/6/9/11/2/12 questions
reproduce all six official percentages (15/20 = 75%, 8/11 = 72.7% → 73%, 8/12 = 66.7% → 67%, 8/9 = 88.9% → 89%,
1/2 = 50%) and sum to 60. No other split fits.
Security's 50% is one miss out of two questions, not a collapse. Testing was 11
questions this time, nearly double #14's six, so 73% is a real result, not a small-sample swing. And
because Data had no misses, Q50 (TransactionInterceptor) must have been scored under Spring Core.
| Attempt #14 | Attempt #15 | Result | |
|---|---|---|---|
| Score | 38/60 (63%) | 46/60 (77%) | Passed — best of fifteen |
| Misses | 22 | 14 | −8 |
| Ticked a false option | 17 | 11 | −6 |
| Stopped short (no false tick) | 5 | 3 | −2 |
| Left blank | 0 | 0 | Held |
| Time | 1h 8m 59s | 52m 34s | 77 minutes unused |
Six of the eight marks came from false ticks, the failure mode the last page said carried the whole gap. The other two came from stopping short less often.
And look at how narrow the remaining misses are. Across the fourteen questions you missed there were 32 false options. You rejected 21 of them. Not one question had two false ticks. Every miss comes down to a single option you believed, or one true option you didn't take.
On the results page, an option loses its letter if it's a correct answer or if you ticked it. So a false option with no letter is definitely a tick you made. But a correct option looks the same whether you ticked it or not. Pages #13 and #14 said "you ticked two" on single-answer misses, and the export can't actually show that: you may have ticked only the wrong option.
So this page says "you ticked X" only for false options, and "stopped short" only when no false option was ticked. The advice doesn't change: on each of the eleven, one false statement looked true to you.
Five of them are sweeping claims: cannot, is not allowed, both, none, the test continues. A sweeping claim falls to a single counterexample. If you can name one property, method or class that breaks it, the option is false.
| Q | The false statement you ticked | The counterexample |
|---|---|---|
| Q5 | "The severity order cannot be changed due to security reasons" | management.endpoint.health.status.order |
| Q34 | SpEL supports querying — "manipulation of objects is not allowed" | expression.setValue(ctx, value) |
| Q53 | "Both fat jar and original jar are executable" | The original jar has no Main-Class and no dependencies |
| Q33 | "None of the mentioned" is excluded from the starter | JUnit 4 was on the list, and it's excluded |
| Q32 | "If any assertion fails, the test continues" | assertAll exists because it doesn't |
| Q10 | "Spring Boot does code generation using an opinionated approach" | Boot's stated goal: "absolutely no code generation" |
| Q46 | ApplicationContext provides "password encryption" | PasswordEncoder — a Spring Security bean |
| Q60 | @ComponentScan adds a bean definition | Scan a package with no @Component classes and you get zero beans |
| Q13 | prePostEnabled = true enables @Secured | securedEnabled is named after the annotation it enables |
| Q44 | @AutoConfigureDataSource | Invented. You ticked it on #6 too |
| Q50 | TransactionStatusAdvice | Invented — two real names glued together |
You finished with 77 minutes to spare. A second pass that only re-checks options containing cannot / only / not allowed / both / none / always costs perhaps ten minutes. On this paper it would have reached five marks.
The three times you stopped short had one shape:
Q28, Q37 and Q45 were all open "which are true" questions with exactly one false option, which you rejected
every time. After that there was nothing left to reject, and every remaining option was true. On Q45 three of
the true options state the same fact three ways (runs regardless of outcome, like a finally block,
suited to cleanup).
The same question, not just the same topic. Four more are close
relatives. Only Q32 (assertAll) and Q34 (the SpEL definition) are new to this site.
| Q | Topic | Already on this site | Then → now |
|---|---|---|---|
| Q28 | True about MVC controllers | Review #13 Q29 · MVC round 2 | Stopped short → stopped short again |
| Q44 | @DataJpaTest real database | Review #6 Q35 · Review #12 | Two fakes ticked → one fake ticked |
| Q13 | Enabling @Secured | Security clinic — "asked three times, missed three times" · Review #5 | Fourth miss |
| Q60 | Adding a bean definition | Review #3, with a drill using these exact four options · Review #4 Q25 | Fourth sitting, third miss |
| Q37 · Q53 | Fat jar vs original jar | Review #3 Q7 · Review #11 Q10 · Boot clinic | "Smaller" ticked on #11 → rejected now, but missed twice |
| Q33 | What starter-test leaves out | Review #1 Q20 — same stem | Answer was "None" on #1 → JUnit 4 this time |
| Q45 | @After advice | Review #3 Q12 & Q13 — "the finally block" | Stopped short |
| Q5 | Health status order | Boot clinic §health · Review #1 | related |
| Q10 | What Boot does and doesn't do | Boot clinic — "what Boot provides, and what it doesn't" | related |
| Q46 | ApplicationContext | Review #10 Q14 · lesson 1 | related |
| Q50 | TransactionInterceptor | Review #13 §tx · Data clinic | related |
Q33 is worth a second look. On #1 the stem was the same and "None of the mentioned" was correct, because that list was JUnit 5, Spring Test and Mockito, and all of those are included. This list added JUnit 4, which is excluded. A remembered answer is only good if the options match too.
Several of these are half-fixed, which is real
progress. On Q44 you dropped replaceProdDb but kept @AutoConfigureDataSource. On Q37 you
rejected "smaller", the option that cost you on #11. One more drill round should finish them.
| Bucket | Marks | What it costs you |
|---|---|---|
| Second pass on sweeping claims | 5 | Zero study — ten of the 77 unused minutes. Q5, Q32, Q33, Q34, Q53 |
| Re-drill the eight same-question repeats | 8 | Q13, Q28, Q33, Q37, Q44, Q45, Q53, Q60 — all already on this site |
| Never tick a name you can't place | 2 | @AutoConfigureDataSource · TransactionStatusAdvice |
The rows overlap (Q33 and Q53 appear in the first two), so don't add them up. Any single row takes you from one mark of margin to three or more. Aim for 48+ next time.
| Q | Topic | What went wrong | The answer |
|---|---|---|---|
| Q5 | Health indicator status — "which two" | + "order cannot be changed" | Custom statuses exist · default DOWN, OUT_OF_SERVICE, UP, UNKNOWN |
| Q10 | What Spring Boot is | + "does code generation" | Stand-alone, production-grade · opinionated |
| Q13 | Enabling @Secured | + prePostEnabled | securedEnabled = true |
| Q28 | True about MVC controllers | Stopped short — 3 were true | Stereotype · no Servlet API dependency · no inheritance |
| Q32 | JUnit 5 assertions | + "the test continues" | Messages optional · assertEquals for objects and primitives · checks the fields |
| Q33 | What starter-test leaves out | + "None of the mentioned" | JUnit 4 (Vintage), dropped in Boot 2.4 |
| Q34 | What SpEL is | + "querying only" | Querying and manipulating an object graph |
| Q37 | Fat jar vs regular jar | Stopped short — 3 were true | Executable · Main-Class + Start-Class · all dependencies |
| Q44 | @DataJpaTest real database | + @AutoConfigureDataSource — invented | @AutoConfigureTestDatabase(replace = Replace.NONE) |
| Q45 | @After advice | Stopped short — 4 were true | Always runs, like finally · cleanup · no return value or exception |
| Q46 | ApplicationContext capabilities | + password encryption | i18n messages · bean factory methods |
| Q50 | The class in the transactional proxy | + TransactionStatusAdvice — invented | TransactionInterceptor |
| Q53 | Fat jar vs original jar | + "both are executable" | You need spring-boot-maven-plugin |
| Q60 | Adding a bean definition | + @ComponentScan | @Bean method · XML <bean> |
Each statement below comes from this paper. Before you answer, name the counterexample — the property, method or class that breaks the claim. If you can name one, the statement is false. If you can't, don't assume it's true; that's the moment to slow down.
| The option says… | Ask | On this paper |
|---|---|---|
| cannot · is not allowed · never | Is there a property or method that does it? | Q5, Q34 |
| both · all · none of the mentioned | Is there one item the claim fails for? | Q53, Q33 |
| continues · always · only | What would the API look like if this were true? | Q32 |
| A class or annotation name you can't place | Is it two real names glued together? | Q44, Q50 |
A true word doesn't make an option true. Q10's option called Boot opinionated (true) and said it does code generation (false). Judge the claim, not the vocabulary around it.
The correct option is the first sentence of the SpEL chapter in the Spring reference: "a powerful expression language that supports querying and manipulating an object graph at runtime". Each "only" option simply deletes half of that sentence.
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext(inventor);
String name = parser.parseExpression("name").getValue(ctx, String.class); // query
parser.parseExpression("name").setValue(ctx, "Nikola Tesla"); // manipulate
parser.parseExpression("name = 'Nikola Tesla'").getValue(ctx); // manipulate, with =
The fourth option is false too. SpEL doesn't replace Java or Kotlin. It's an
expression language you use from Java: in @Value("#{…}"), in @PreAuthorize,
in @Cacheable(key = "#id"), and on its own through SpelExpressionParser, with no
ApplicationContext at all.
getValue() reads, setValue() writes. An option that allows "only" one of them is half the definition.@After advice stopped short review #3 · Q12 & Q13@After prevents the target method executing".Put the true options side by side: "runs whether the method completes or throws",
"is like a finally block", "suitable for cleanup". That's one fact written three ways. Options
that restate each other stand or fall together, so once one is true, take them all.
| Advice | Runs when | Sees return value | Sees exception | Can stop the target |
|---|---|---|---|---|
@Before | Before the method | No | No | Only by throwing |
@AfterReturning | After a normal return | Yes — returning= | No | No |
@AfterThrowing | After an exception | No | Yes — throwing= | No |
@After | Always — the finally | No | No | No |
@Around | Around the call | Yes | Yes | Yes — skip proceed() |
Review #3 said it in one line, and it still holds: "@After is the finally block:
always runs, sees nothing."
@After = finally: always runs, sees nothing, stops nothing. Return value → @AfterReturning. Exception → @AfterThrowing. Change either → @Around.ApplicationContext extends | Which gives it |
|---|---|
ListableBeanFactory | Bean factory methods — getBean, getBeansOfType |
HierarchicalBeanFactory | Parent contexts |
MessageSource | Message resolution, i18n |
ApplicationEventPublisher | Publishing events to listeners |
ResourcePatternResolver | Loading resources — classpath*:… |
EnvironmentCapable | getEnvironment() — profiles and properties |
Both false options are jobs a bean does: PasswordEncoder (Spring
Security) and DataSource. Any bean can live in the context, so "the context can hold one" doesn't
make it a capability of the context. If it isn't in the table, it's a bean's job.
TransactionInterceptor — the advice that begins, commits and rolls backTransactionStatusAdvice, which doesn't exist.
You correctly rejected the other fake, PlatformTransactionManagerAdvisor, and
TransactionTemplate.| Style | You write | What does the work |
|---|---|---|
| Declarative | @Transactional | TransactionInterceptor, inside the AOP proxy |
| Programmatic | transactionTemplate.execute(status -> …) | TransactionTemplate — no proxy |
| Programmatic, low level | getTransaction() / commit() / rollback() | PlatformTransactionManager + TransactionStatus |
How the declarative pieces connect: @EnableTransactionManagement
registers an advisor (BeanFactoryTransactionAttributeSourceAdvisor). Its pointcut matches
@Transactional methods, and its advice is TransactionInterceptor, a
MethodInterceptor. The interceptor asks the transaction manager to begin, calls
proceed(), then commits or rolls back.
The tell for both fakes: each one glues two real names together —
TransactionStatus + Advice, PlatformTransactionManager +
Advisor. Real parts don't make a real class.
TransactionInterceptor in a proxy. Programmatic = TransactionTemplate. A name made of two real class names is invented.@Bean and <bean> add definitions; @ComponentScan only says where to look@ComponentScan alongside the two correct options.
Review #3 has a drill with these exact four options and this answer.The test that settles it: point @ComponentScan at a package with no
@Component classes. It registers zero beans. The definition comes from @Component
(or @Service, @Repository, @Controller) on each class. Scanning only finds
them. That's why the bank counts the stereotype, not the scan. new creates an object the container
never sees.
| Adds a bean definition | Doesn't |
|---|---|
@Bean method in a @Configuration class | @ComponentScan on its own |
XML <bean> | new |
A scanned @Component-family class | @Autowired — it uses beans, it doesn't define them |
@Import(OtherConfig.class) | |
GenericApplicationContext.registerBean(…) · BeanDefinitionRegistryPostProcessor |
@Bean method, or <bean>. @ComponentScan finds; it doesn't define.DOWN → OUT_OF_SERVICE → UP → UNKNOWN| Option | Verdict | Why |
|---|---|---|
| Custom status values can be created | True | Health.status("FATAL") or new Status("FATAL") |
| The least severe status is the top-level status | False | The most severe wins |
| The order cannot be changed, for security reasons | False | management.endpoint.health.status.order |
| Built-in: DOWN, OUT_OF_SERVICE, UP, UNKNOWN, most severe first | True | SimpleStatusAggregator's default order |
| The last status in a sorted list is used | False | The first one is. The sorted list is real; "last" is the lie |
Adding a custom status end to end:
@Component
class DiskHealthIndicator implements HealthIndicator {
public Health health() {
return Health.status("FATAL").withDetail("disk", "corrupted").build();
}
}
# 1. list it in the order, most severe first; the list replaces the default
management.endpoint.health.status.order=fatal,down,out-of-service,up,unknown
# 2. give it an HTTP code; an unmapped status returns 200
management.endpoint.health.status.http-mapping.fatal=503
Two traps outside the question. (1) A status that isn't in the order is
ignored. The aggregator filters out unknown statuses before choosing, so if you forget step 1, a
FATAL indicator leaves the endpoint UP. (2) The bank's explanation contradicts its
own answer. Its prose lists UNKNOWN before UP, but the option it marks correct puts UP before UNKNOWN. So does
Spring Boot's source. Trust the option.
The option attaches a true word to a false claim. Opinionated is straight from the true option above it, and that's what made the sentence sound right. Rule the claim — does code generation — on its own. Boot's project page lists its goals, and one of them is literally "absolutely no code generation and no requirement for XML configuration".
What it does instead: auto-configuration creates beans at runtime from
@Conditional classes that ship pre-compiled in spring-boot-autoconfigure. Nothing is ever
written into your source tree. start.spring.io generates a project skeleton, but that's the Initializr, a
separate tool, not Boot.
Original jar — app.jar.original | Fat jar — app.jar | |
|---|---|---|
| Built by | maven-jar-plugin | spring-boot-maven-plugin, repackage goal |
| Contains | Your classes and resources only | Your classes + every dependency + the loader |
java -jar | No | Yes |
MANIFEST.MF | No Main-Class | Main-Class: …JarLauncher · Start-Class: your app |
| Size | Small | Larger — always |
app.jar
├── META-INF/MANIFEST.MF Main-Class: org.springframework.boot.loader.JarLauncher
│ Start-Class: com.example.App
├── BOOT-INF/classes/ your compiled code
├── BOOT-INF/lib/ every dependency, as nested jars
└── org/springframework/boot/loader/ the launcher
Earlier options can answer later questions. Q37 came sixteen questions before Q53 and stated the fact outright. When a later option contradicts something you've already judged true, one of them has to be wrong.
java -jar. Main-Class = JarLauncher, Start-Class = your app. Made by spring-boot-maven-plugin.assertAll existsassertNull instead".Every assertXxx method throws AssertionFailedError (from opentest4j) when
it fails. An exception ends the method, so nothing after it runs. If assertions carried on after a failure,
assertAll would have no reason to exist. It runs every lambda, collects the failures, and reports
them together.
@Test
void createsUser() {
User user = service.createUser("John", "Doe");
assertNotNull(user, "User should not be null"); // fails → throws → test ends here
assertAll("name", // every lambda runs; all failures reported
() -> assertEquals("John", user.getFirstName()),
() -> assertEquals("Doe", user.getLastName()));
}
| Option | Verdict |
|---|---|
assertEquals checks the first and last name match | True |
| Messages are optional and shown when an assertion fails | True |
assertEquals compares objects as well as primitives | True — overloads for primitives, equals() for objects |
| The test continues after a failed assertion | False — it throws |
assertNull should replace assertNotNull | False — the user must exist |
Two details this bank likes: in JUnit 5 the message is the last argument
(JUnit 4 put it first), and it can be a Supplier<String>, so an expensive message is only built
if the assertion fails.
assertAll runs everything and reports every failure. The message goes last in JUnit 5.spring-boot-starter-test leaves out ticked a false option review #1 · Q20 — same stem| In the starter (Boot 2.4+) | Not in it |
|---|---|
| JUnit 5 (Jupiter) | JUnit 4 Vintage engine — removed in 2.4 |
| Spring Test + Spring Boot Test | PowerMock, EasyMock |
| AssertJ · Hamcrest | Testcontainers |
| Mockito | Your database driver |
| JSONassert · JsonPath · XMLUnit |
<!-- still running JUnit 4 tests? add the engine back yourself -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
Same stem, different options, different answer. "None of the mentioned" is only true if every item listed is actually included. Check each one before you reach for it. (In Boot 2.2 and 2.3 the Vintage engine was included. Your exam targets 2.5.)
@DataJpaTest invented name review #6 · Q35 — same fake@AutoConfigureTestDatabase(replace = Replace.NONE) — @AutoConfigureDataSource still doesn't exist@AutoConfigureDataSource(replace = Replace.NONE).
On #6 you ticked it and replaceProdDb. This time you rejected
replaceProdDb and replaceDb, so half of this is fixed.How to find the real one: @DataJpaTest is meta-annotated with
@AutoConfigureTestDatabase(replace = Replace.ANY). So the way to switch the replacement off is that
same annotation, declared on your test class with a different value. @DataJpaTest has no attribute
for it at all.
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class OrderRepositoryTest { … }
Replace | Behaviour |
|---|---|
ANY (default) | Replace any DataSource — auto-configured or your own |
AUTO_CONFIGURED | Replace only the auto-configured one |
NONE | Keep the configured DataSource |
The same switch exists as a property, spring.test.database.replace=none. And
@JdbcTest carries the same meta-annotation, so the same fix works there too.
@AutoConfigureTestDatabase(replace = NONE). The real name has Test in it.| Option | Verdict | Why |
|---|---|---|
@Controller is a stereotype annotation | True | Meta-annotated with @Component, so scanning finds it |
| No direct dependency on Servlet or Portlet APIs | True | The older Spring reference's own words. You can take HttpServletRequest as a parameter; you don't have to |
| No specific class or interface inheritance required | True | No AbstractController, no Controller interface |
| All endpoint paths must share the same prefix | False | A class-level @RequestMapping is optional |
This is the "one false option" shape again. The false option doesn't sound like the other three. Once you've rejected it, nothing is left to reject, so take everything else. Stopping at two out of caution costs exactly as much as a wrong tick.
@RequestMapping prefix is optional.@Secured ticked a false option 4th sitting · Security clinic §methodsecuredEnabled switches on @Secured — prePostEnabled never does@EnableMethodSecurity(prePostEnabled = true).
The Security clinic notes this topic was
"asked three times, missed three times". This makes four.| Attribute | Switches on | Default — 5.x @EnableGlobalMethodSecurity | Default — 6 @EnableMethodSecurity |
|---|---|---|---|
prePostEnabled | @PreAuthorize · @PostAuthorize · @PreFilter · @PostFilter | false | true |
securedEnabled | @Secured | false | false |
jsr250Enabled | @RolesAllowed · @PermitAll · @DenyAll | false | false |
The name gives it away: each attribute is named after the annotations it enables.
secured → @Secured, jsr250 → the JSR-250 annotations, prePost →
@Pre…/@Post…. The correct option also set jsr250Enabled = true; switching
on a second family does no harm.
Version note: the question uses @EnableMethodSecurity (Security
5.6+). On a 5.x-era exam you're more likely to see @EnableGlobalMethodSecurity(securedEnabled = true).
The attribute names are the same.
securedEnabled → @Secured. prePostEnabled → @Pre/@Post. jsr250Enabled → @RolesAllowed.management.endpoint.health.status.order. Default DOWN > OUT_OF_SERVICE > UP > UNKNOWN; the first (worst) winssecuredEnabled → @Secured · prePostEnabled → @Pre/@Post · jsr250Enabled → @RolesAllowedassertAll reports every failure; the message goes lastspring-boot-starter-test leaves out only JUnit 4 Vintage (since 2.4). Read the list before "None of the mentioned"getValue() reads, setValue() writesMain-Class: JarLauncher and Start-Class: your appspring-boot-maven-plugin to make it@AutoConfigureTestDatabase(replace = Replace.NONE) keeps the real DataSource. @AutoConfigureDataSource doesn't exist@After = finally: always runs, sees neither return value nor exception, stops nothingTransactionInterceptor in a proxy. @ComponentScan finds beans; @Bean, <bean> and stereotypes define themEleven questions with no topic label to prime you. For every option with cannot, only, both, none or a name you can't place, say the counterexample out loud before you tick.