44/60. That beats attempt #4's 72%, which had stood as your ceiling for eight days. You need +2 — the smallest gap of the series. And the reason is not a mystery: you slowed down. 28m46s yesterday evening → 42m30s today, and the misses fell from 18 to 16 while three sections rose. Spring Core broke 71% for the first time in nine attempts. Two things are left, and both are things you already have: Actuator, which has now cost you marks in four consecutive papers, and nine of these sixteen are facts an earlier review already taught you.
Three sections up, and one of them broke a nine-attempt ceiling. Spring Core had never once been
above 71% across eight papers — today it hit 75%. Spring MVC recovered from yesterday's 40% to 57%
without a clinic, just from review #8's return-types table. Data reached 81%, its best since attempt #4.
Testing held 100% for a second consecutive paper.
Spring Boot is now the problem, and it is specifically Actuator. 56%, five misses, and
Q13 and Q15 are the same distinction asked twice — enabled versus exposed. That pairing
has now appeared in reviews #6, #7, #8 and this one. It is the single most repeated topic in the entire series.
Every review since #5 has said the same thing about the clock, and
this is the first paper where it changed. You spent 13 minutes 44 seconds longer than yesterday evening and
got your best result in nine attempts: misses down from 18 to 16, three sections up, a nine-attempt ceiling
broken.
That is not proof on its own — one paper, and the question mix differs each time. But it points
the same way as everything else in the data: 10 of today's 16 misses were still multiple ticks on a
one-answer question, which is the error that extra time directly prevents. You are still using only 33% of
the clock. There are 87 minutes sitting unused, and the two marks you need are almost certainly in
them.
You asked to be clear on how to improve from here. With a gap of two, the question isn't "what should I study" — it's "which two marks are cheapest". Here is where the sixteen sit:
| Bucket | Marks | What it costs to fix |
|---|---|---|
| ① Facts an earlier review already taught you Q5, Q13, Q14, Q15, Q18, Q28, Q36, Q45, Q57 |
9 | Re-cover the six checklists + the Data clinic. ~45 minutes, no new material. |
| ② Actuator specifically Q13 and Q15 — both the enabled vs exposed distinction |
2 | One table, below. Ten minutes, and it is exactly the gap. |
③ Names that don't existfiltersNone(), @EnableAnnotationTransaction, @AnnotationDrivenTransaction |
2 | One question per option. Zero minutes. Down from 4 marks last paper. |
| ④ Genuinely new material (REST principles, Commons Logging, @Conditional, "lightweight", return types) | 5 | Real study — worth doing, not needed to pass. |
Bucket ② alone is the pass. Two questions on this
paper — Q13 and Q15 — turned on one sentence: every Actuator endpoint except shutdown is
enabled, but only health is exposed over HTTP. Learn that sentence and, on this paper, you
score 46.
| Section | #5 · 19 Aug | #6 · 25 am | #7 · 25 pm | #8 · 25 eve | #9 · 26 Aug | Verdict |
|---|---|---|---|---|---|---|
| Testing | 88% | 70% | 86% | 100% | 100% | Done — held twice |
| Data Management | 36% | 75% | 57% | 75% | 81% | Best since #4 — the clinic holds |
| Spring Core | 71% | 57% | 65% | 61% | 75% | Broke its nine-attempt ceiling |
| Spring Security | 60% | 33% | 100% | 100% | 75% | Slipped, but off a high base |
| Spring MVC | 67% | 67% | 75% | 40% | 57% | Recovering — still the weakest topic |
| Spring Boot | 92% | 40% | 64% | 71% | 56% | Now the worst — and it's Actuator |
| Overall | 70% | 58% | 70% | 70% | 73% | Best of nine |
filtersNone(),
@EnableAnnotationTransaction, @AnnotationDrivenTransaction. The habit is working;
keep it.Enabled and exposed are two independent switches. Q13 and Q15 both turned on this, and so did questions in reviews #6, #7 and #8.
| Enabled — does the endpoint exist? | Exposed — can you reach it? | |
|---|---|---|
| Property | management.endpoint.<id>.enabledglobally: management.endpoints.enabled-by-default | management.endpoints.web.exposure.includemanagement.endpoints.jmx.exposure.include |
| Default | all enabled except shutdown | HTTP: health onlyJMX: * (everything) |
So info is… | enabled ✅ | not exposed over HTTP ❌ (Boot 2.5+) |
And loggers is… | enabled ✅ | JMX yes, HTTP no until you add it |
Three sentences to memorise:
(1) Everything is enabled except shutdown.
(2) Over HTTP only health is exposed; over JMX everything is.
(3) The exposure key contains web or jmx; the enablement key does not.
Your answer is reconstructed from the exported result — if a line looks wrong, tell me and I'll fix it. Click the topic to jump to the full explanation.
| Q | Topic | What you answered | The answer |
|---|---|---|---|
| Q1 | What REST is | "REST is Stateful" + "REST is Relative" | REST is interoperable — and stateless, and a style not a protocol |
| Q2 | Boot's internal logging API | "Log4J2" + "SLF4J" | Commons Logging (Logback is the default implementation) |
| Q4 | What @Conditional is for | "environment config" + "app run statements" | Conditional bean registration |
| Q5 | "@Transactional supports SpEL" | "True" — taught in #2 and #5 | False — fixed types only |
| Q13 | The loggers endpoint | "level can be changed only via HTTP" | Lists all loggers · changeable via HTTP or JMX |
| Q14 | @RestController equals… | Both @SpringBootApplication options | @Controller + @ResponseBody |
| Q15 | The info endpoint | "only authenticated users" + "can change a property" | Build/SCM info · arbitrary app information |
| Q17 | Bypassing the filter chain | "filtersNone()" (invented) + "anonymous()" | web.ignoring().antMatchers(…) |
| Q18 | Resolving autowiring ambiguity | Under-selected — missed one of three | @Primary · @Qualifier("cat") on the field · rename the field |
| Q22 | Why Spring is "lightweight" | "no runtime needed" + "can't use POJOs" | No JEE server · small modular JARs · low overhead |
| Q28 | What spring.factories can register | Under-selected — all four were correct | Listeners · EnvironmentPostProcessors · auto-config filters · auto-config classes |
| Q36 | PreparedStatementCreator | The RowMapper and ResultSetExtractor descriptions | Build a PreparedStatement from the given Connection |
| Q45 | Why NamedParameterJdbcTemplate | Three wrong options ticked | Named parameters → readability |
| Q47 | Enabling annotation-driven transactions | Two invented annotations | @Configuration + @EnableTransactionManagement |
| Q56 | Valid controller methods | "@RequestBody on the return type" | @RequestBody is input only; @ResponseBody is output |
| Q57 | @PostConstruct / @PreDestroy behaviour | "always invoked regardless of scope" | Prototypes get no @PreDestroy · called once · before destroy() |
56% — now your weakest section, and two of the five are the single table printed above.
info endpoint enabled vs exposed · #6, #7, #8info is strictly read-only. The endpoint that changes something at runtime is
loggers — which is Q13, on the same paper.Three ways to put data into info, all worth recognising:
| Source | How |
|---|---|
| Static properties | info.app.name=…, info.app.version=… in application.properties |
| Build information | the Boot Maven/Gradle plugin generates build-info.properties |
| Source control | git.properties from the git-commit-id plugin |
| Anything dynamic | a @Component implementing InfoContributor |
@Component
public class CustomInfo implements InfoContributor {
@Override public void contribute(Info.Builder builder) {
builder.withDetail("region", "eu-west-2");
}
}
The option that looks true and isn't: "it is not enabled by default". info
is enabled — every endpoint except shutdown is. What changed in Boot 2.5 is that
info stopped being exposed over HTTP by default; in Boot 2.0 both health and
info were. Since 2.5, only health is. Enablement ≠ exposure, and this question exists
solely to test that you don't conflate them.
Security isn't specific to info either — it applies to every endpoint and is
configured with Spring Security, not by the endpoint itself.
info is enabled, read-only, and not HTTP-exposed since Boot 2.5. Populate it with info.* properties, build-info, git.properties, or an InfoContributor.loggers endpoint Actuator · fourth paper runningloggers is the endpoint that makes Actuator genuinely operational rather than
informational — it is read and write:
# read every logger and its level
GET /actuator/loggers
# read one
GET /actuator/loggers/com.example
# change one at runtime — no restart
POST /actuator/loggers/com.example
Content-Type: application/json
{"configuredLevel": "DEBUG"}
| Field | Meaning |
|---|---|
configuredLevel | what you explicitly set (may be null) |
effectiveLevel | what actually applies — inherited from the parent logger if not configured |
Two details that get asked. Posting {"configuredLevel": null} resets the logger to
inherit from its parent — it does not restore the startup level. And the exposure rule from the table above
applies here: loggers is enabled, visible over JMX by default, but needs
management.endpoints.web.exposure.include=loggers before HTTP will serve it.
loggers is the writable one: GET to list, POST to change, over HTTP or JMX. configuredLevel vs effectiveLevel.spring.factories can register taught in review #7Review #7 covered the file's location (META-INF/spring.factories); this question tests
its range. It is a general extension registry, not an auto-configuration-only file:
| Key | Registers |
|---|---|
EnableAutoConfiguration | auto-configuration classes — how custom starters work |
AutoConfigurationImportFilter | filters that cut candidates out early, before conditions are evaluated |
ApplicationListener | event listeners active during startup |
EnvironmentPostProcessor | code that mutates the Environment before the context exists |
ApplicationContextInitializer | context customisation before refresh |
FailureAnalyzer | turns startup exceptions into readable diagnostics |
Why it's a file rather than beans: every one of those runs before the
ApplicationContext exists, so there is no container to look them up in. SpringFactoriesLoader
reads the file from every JAR on the classpath and merges the entries.
The version fact from review #7 still applies: Boot 2.7 moved auto-configuration registration
only to META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports; every
other key stayed in spring.factories.
spring.factories registers anything that must exist before the context does — auto-config, filters, listeners, environment processors, failure analyzers.There are two layers, and the question is about the upper one:
| Layer | What | Why |
|---|---|---|
| API used by Spring's own code | Commons Logging (JCL) — org.apache.commons.logging.Log | keeps the framework decoupled from any implementation |
| Implementation that writes the output | Logback by default (via spring-boot-starter-logging) | swappable for Log4J2 or JUL |
Spring's own code
→ Commons Logging (the API it compiles against)
→ Logback (default)
or Log4J2 (swap the starter)
or java.util.logging
The historical detail that makes it stick: Spring committed to Commons Logging
long before SLF4J existed, and its public API still exposes it. Since Spring 5, spring-jcl is a
reimplementation of the Commons Logging API that delegates straight to SLF4J or Log4J2 if present —
so you get JCL's interface with modern routing, and no commons-logging JAR.
In your code you should still use SLF4J (LoggerFactory.getLogger(...)) — the
question is specifically about Spring's internals.
@Conditional is for@Conditional(SomeCondition.class) asks a Condition implementation
"should this bean definition be registered?" during context startup. It's evaluated once, at bean-definition
time — there is no runtime "app run statement" concept anywhere in it.
public class OnLinuxCondition implements Condition {
public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata md) {
return ctx.getEnvironment().getProperty("os.name").contains("Linux");
}
}
@Bean
@Conditional(OnLinuxCondition.class)
public MyService myService() { … }
The connection worth making: @Conditional is the engine behind
all of Boot's auto-configuration. Every @ConditionalOn… annotation is just
@Conditional with a pre-written condition:
| Annotation | Registers the bean when… |
|---|---|
@ConditionalOnClass | a class is on the classpath |
@ConditionalOnMissingBean | no bean of that type exists — this is how Boot "backs off" |
@ConditionalOnProperty | a property has a given value |
@ConditionalOnWebApplication | running in a web context |
@ConditionalOnExpression | a SpEL expression is true |
And note the neighbour: @Profile is itself a @Conditional —
@Conditional(ProfileCondition.class). So "environment-based loading" is one instance of
@Conditional, not its definition.
81% — your best Data score since attempt #4. The clinic is holding. Three of these four are in it or in an earlier checklist.
@Transactional supports SpEL" taught in #2 and #5Look at the attribute list and it's obvious there's nowhere for an expression to go:
| Attribute | Type |
|---|---|
propagation / isolation | enum constants |
timeout | int |
readOnly | boolean |
rollbackFor / noRollbackFor | Class<? extends Throwable>[] |
transactionManager | a plain String bean name |
The comparison the exam actually wants — which annotations do take SpEL:
| SpEL supported | No SpEL |
|---|---|
@Value("#{…}") | @Transactional |
@PreAuthorize / @PostAuthorize | @Secured (role names only) |
@Cacheable(key, condition, unless) | @Scheduled (placeholders only) |
@ConditionalOnExpression | @Profile (strings + !) |
There is a rough logic to it: annotations that make a per-invocation decision take SpEL
(which user? which cache key? is this expression true right now?). @Transactional's attributes are
resolved once when the proxy is built, so there's nothing to evaluate per call.
@Configuration + @EnableTransactionManagement@EnableAnnotationTransaction and
@AnnotationDrivenTransaction — neither exists. Both are built from the right
words in the wrong order, which is exactly the shape to distrust.The pattern is Spring-wide and worth generalising: feature switches are always
@EnableXxx on a @Configuration class.
| Annotation | Switches on |
|---|---|
@EnableTransactionManagement | @Transactional processing |
@EnableAspectJAutoProxy | @Aspect processing |
@EnableCaching | @Cacheable and friends |
@EnableScheduling | @Scheduled |
@EnableAsync | @Async |
@EnableWebMvc | MVC infrastructure beans |
@EnableJpaRepositories | Spring Data repository proxies |
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean DataSource dataSource() { … }
@Bean PlatformTransactionManager transactionManager(DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
What it actually does: registers an infrastructure bean that creates AOP proxies around
@Transactional classes. Without it the annotation is inert — no error, just no transactions,
the same silent failure as putting @Transactional on a private method.
And in Boot you never write it —
TransactionAutoConfiguration adds it for you. That's worth knowing as its own fact, because
"which of these do you need in a Boot app?" is a stock question.
@Enable, it isn't a Spring feature switch.PreparedStatementCreator callbacks · Data clinic §6PreparedStatement from the Connection you're handed — SQL and parametersResultSetExtractor and
RowMapper. Those describe reading results. This callback runs
before execution.The clinic covered the three reading callbacks; this is the writing one. Ordered by when they fire:
| Callback | When | Given | Returns |
|---|---|---|---|
PreparedStatementCreator | before execution | a Connection | a configured PreparedStatement |
PreparedStatementSetter | before execution | a PreparedStatement | void — just binds parameters |
RowMapper<T> | after, once per row | a positioned ResultSet | T |
RowCallbackHandler | after, once per row | a positioned ResultSet | void |
ResultSetExtractor<T> | after, once | the whole ResultSet | T |
Why you'd ever need it — the canonical case is capturing a generated key, which
the simple update(sql, args…) form can't do:
KeyHolder keys = new GeneratedKeyHolder();
jdbc.update(con -> {
PreparedStatement ps = con.prepareStatement(
"insert into employees(name, dept) values(?, ?)",
Statement.RETURN_GENERATED_KEYS); // ← needs the Connection in hand
ps.setString(1, emp.name());
ps.setString(2, emp.dept());
return ps;
}, keys);
long id = keys.getKey().longValue();
Two rules shared with every JdbcTemplate callback: do not execute it yourself
(duplicate rows on INSERT), and do not close the statement or connection —
JdbcTemplate owns the lifecycle.
NamedParameterJdbcTemplate #6 and the Data clinicWhy "prevents SQL injection" is wrong. Both templates use
PreparedStatement underneath, so bound parameters are safe in both — the protection
comes from prepared statements, not from named parameters. And neither protects you if you concatenate user input
into the SQL string:
// unsafe in BOTH templates
String sql = "SELECT * FROM users WHERE name = '" + userInput + "'";
// safe in BOTH — the value is bound, not interpolated
jdbc.query("SELECT * FROM users WHERE name = ?", mapper, userInput);
named.query("SELECT * FROM users WHERE name = :name", Map.of("name", userInput), mapper);
So the difference between the two templates is readability and maintainability only:
JdbcTemplate | NamedParameterJdbcTemplate | |
|---|---|---|
| Placeholder | ? — positional | :name — named |
| Arguments | varargs, order matters | Map or SqlParameterSource |
| With 8 parameters | easy to transpose two silently | self-documenting |
It wraps a JdbcTemplate (reachable via getJdbcOperations()) and
translates :name into positional ? before executing — so it is strictly a convenience
layer, which is exactly why "required for executing SQL" and "handles all SQL including DDL" are both wrong.
40% → 57% with no clinic, just review #8's return-types table. Still the weakest topic, but moving.
@RestController is equivalent to #7 and #8@Controller + @ResponseBody@SpringBootApplication.
That's the application entry point — three annotations about bootstrapping, nothing to do with
controllers.The source is two lines long:
@Target(ElementType.TYPE)
@Controller // ← makes it a Spring MVC controller bean
@ResponseBody // ← makes every method write to the response body
public @interface RestController { … }
The consequence is the fork you've now met several times:
| Class annotation | return "list"; means |
|---|---|
@Controller | a logical view name → ViewResolver → /WEB-INF/views/list.jsp |
@RestController | the literal text "list" written to the response body |
The practical rule: if a controller must return views from some methods and JSON
from others, use @Controller and put @ResponseBody on the individual JSON methods.
@RestController is all-or-nothing for the class.
And note @RequestMapping is not part of it — you still declare mappings
yourself. That was the fourth distractor.
@RequestBody is input only — it can never annotate a return typepublic @RequestBody MyDataDto getMyData().
You correctly accepted the other three, including the HttpHeaders return type — the
genuinely surprising one.The two annotations are a matched pair pointing in opposite directions:
| Annotation | Direction | Legal on |
|---|---|---|
@RequestBody | IN — deserialise the request body | a method parameter |
@ResponseBody | OUT — serialise the return value | a method or the return type |
@PostMapping("/products")
public @ResponseBody Product create(@RequestBody Product incoming) { … }
// ^^^^^^^^^^^^^ OUT ^^^^^^^^^^^^ IN
Why HttpHeaders as a return type is valid — the surprise in this
question: Spring MVC's list of supported return types is long, and HttpHeaders is on it, producing a
header-only response with an empty body. It's legal but rarely what you want;
ResponseEntity<T> gives you status, headers and body and is the idiomatic choice.
Also valid in that question: returning a Map (treated as model attributes under
@Controller, serialised to JSON under @ResponseBody), and taking an
HttpSession parameter — servlet objects go in as parameters, which is the rule that has now
appeared on three consecutive papers.
The six architectural constraints, which is the list these questions are drawn from:
| Constraint | Means |
|---|---|
| Stateless | the server keeps no client session — every request carries everything it needs |
| Client–server | separate concerns, independently evolvable |
| Uniform interface | standard verbs and resource URIs |
| Cacheable | responses declare their cacheability |
| Layered system | proxies and gateways can sit in between invisibly |
| Code on demand (optional) | the server may ship executable code |
"Style, not protocol" is the other stock answer and worth holding alongside: REST is a set of constraints described by Roy Fielding; HTTP is the protocol it's normally carried over. SOAP, by contrast, is a protocol with a specification, an envelope format and a WSDL contract — which is exactly the comparison these questions are usually building toward.
Interoperability follows from the other constraints: standard HTTP verbs plus self-describing representations (JSON/XML) mean a Python client and a Java server need no shared runtime, stubs or contract-generation step.
75% — the first time Core has been above 71% in nine attempts. The ceiling is broken.
@Primary, @Qualifier("cat") on the field, or rename the fieldTwo IAnimal beans exist, so injection by type is ambiguous. Spring's resolution order,
in full:
| Step | Mechanism | In this question |
|---|---|---|
| 1 | match by type | two candidates — ambiguous |
| 2 | @Qualifier narrows the candidates | @Qualifier("cat") on the field ✅ |
| 3 | @Primary wins among what's left | @Primary on Cat or Dog ✅ |
| 4 | @Priority | — |
| 5 | injection-point name vs bean names | rename the field to cat or dog ✅ |
| 6 | otherwise | NoUniqueBeanDefinitionException |
@Component class Person {
@Autowired private IAnimal cat; // bean name "cat" → resolved by step 5
}
Why the wrong option is wrong: @Qualifier("animal") on
Dog or Cat names a bean called animal, and no such bean exists — the two
beans are named dog and cat (the class name, decapitalised). Note also that
@Qualifier is placed at the injection point here, not on the candidate class.
Step 5 is why a constructor parameter called myService quietly picks up the bean named
myService — behaviour that looks like magic until you know the fallback exists.
@PostConstruct and @PreDestroy behaviour #5, #6, #8@PreDestroy runs before destroy() · never for prototypesThe callback order, both directions. Annotation first, then interface, then the XML/@Bean attribute:
| Initialisation (after DI) | Destruction (on context close) |
|---|---|
1. @PostConstruct | 1. @PreDestroy |
2. InitializingBean.afterPropertiesSet() | 2. DisposableBean.destroy() |
3. @Bean(initMethod = "…") | 3. @Bean(destroyMethod = "…") |
Why prototypes get no @PreDestroy: the container creates a prototype,
injects it, hands it over and forgets it — it keeps no reference, so it has nothing to call back on. The
caller owns the cleanup. Singletons are held for the context's lifetime, so their destruction callbacks fire on
close.
Worth pairing with the constraints from review #8: void return, no parameters, not
static, any visibility. Between the two questions you now have the whole contract.
"Lightweight" is a historical claim, aimed at EJB 2.x, and it means three specific things:
| Sense | Meaning | The EJB 2.x contrast |
|---|---|---|
| Non-invasive | your classes are POJOs — no framework superclass or interface required | beans had to implement javax.ejb.* interfaces |
| No JEE server | a plain servlet container or embedded Tomcat is enough | required WebLogic, WebSphere or JBoss |
| Modular / low overhead | small JARs, take only what you need; fast startup | a full application server per deployment |
Where the distractors go wrong. "No runtime at all" is nonsense — you always need a JVM; the claim is about not needing an application server. "You can't use POJOs" is the exact inverse of non-invasiveness. And "you are required to extend a Spring class" describes EJB 2.x, which is precisely what Spring was built to escape.
Notice the shape of both your ticks: an absolute negative ("don't need any", "can't use"). That's protocol eliminator (b) — on a question asking why something is good, options that describe a limitation are usually the distractors.
100% → 75%, but only one miss — and it's a distinction genuinely worth knowing.
web.ignoring().antMatchers("/public/**")filtersNone() (doesn't exist) and anonymous().
anonymous() is real, but it's the opposite of bypassing — it runs the whole chain and
assigns an anonymous identity.The distinction the question is really testing — bypass versus permit:
web.ignoring() | permitAll() | |
|---|---|---|
| Filter chain | never runs | runs in full |
| Authentication checked | no | no |
| CSRF protection | none | applied |
| Security headers | none | applied |
SecurityContext | never populated | populated (anonymous) |
| Intended for | static assets — CSS, JS, images | public endpoints |
@Override
public void configure(WebSecurity web) { // note: WebSecurity, not HttpSecurity
web.ignoring().antMatchers("/css/**", "/js/**", "/images/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public/**").permitAll() // still fully filtered
.anyRequest().authenticated();
}
The tell is the argument type: web.ignoring() is configured on
WebSecurity — the object that decides which requests enter the chain at all —
while permitAll() lives on HttpSecurity, which configures the chain's behaviour
once a request is inside it.
Real-world advice worth carrying: prefer permitAll(). web.ignoring()
strips security headers and CSRF too, so it's genuinely only appropriate for static assets — and Spring Security
logs a warning if you use it on anything else. In the modern lambda DSL (Security 5.7+, replacing
WebSecurityConfigurerAdapter) the same thing is a WebSecurityCustomizer bean.
WebSecurity.ignoring() = never enters the chain (static assets). HttpSecurity.permitAll() = enters and is allowed (public endpoints).shutdown; over HTTP only health is exposed, over JMX everything isinfo is enabled, read-only, HTTP-hidden since Boot 2.5; fed by info.*, build-info, git.properties, InfoContributorloggers is the writable endpoint: GET to list, POST to change, over HTTP or JMX; configuredLevel vs effectiveLevel@ConditionalOnX and @Profile is built on itspring.factories registers auto-config classes, import filters, listeners, EnvironmentPostProcessors, initializers, failure analyzersHttpHeaders is a valid return typeNoUniqueBeanDefinitionException; bean name = decapitalised class nameWebSecurity.ignoring() skips the chain entirely (no CSRF, no headers — static assets); HttpSecurity.permitAll() runs it and allows accessweb/jmx, the enablement key
doesn't. On this paper that is Q13 and Q15 — exactly the two marks you need, and it's ten
minutes.
(2) Re-cover the six checklists and the Data clinic.
Nine of today's sixteen are in there. That's 45 minutes and no new material.
(3) Keep slowing down. You went from 29 to 42 minutes and scored your best result in nine attempts —
while still leaving 87 minutes unused and still losing 10 marks to multiple ticks on one-answer
questions. Target 60+ minutes next time and read every option as its own true/false.
(4) Then, and only then, new material.
Lesson 16 · Actuator and
Lesson 12 · REST cover Spring Boot (56%) and Spring MVC (57%), your two
weakest sections — but neither is needed for the +2.
Finished: Testing (100% twice running). Strong: Data 81%, Core 75% — and Core has never been this
high. Recovering: MVC 40 → 57 in a day, Security 75% off a 100% base.
The work: Spring Boot at 56%, which is mostly Actuator, which is mostly one table.
Your score has moved 58 → 70 → 70 → 73 across four papers while the floor under it rose from 33% to 56%.
You are two marks away, and nine of your sixteen misses were things you had already written down.
The remaining gap is not knowledge — it's retrieval and pace.