You scored 42 / 60 = 70% — your second-best result, one mark off attempt #4. Pass is 76% (46/60), so you need +4. But the headline number hides the real finding: Data Management fell from 91% to 36% in a single day, and it alone accounts for 7 of your 18 misses. Nothing about your knowledge collapsed overnight — this paper simply asked the transaction and repository questions that reviews #1 and #2 covered and you haven't revisited since. That's the lesson of this attempt: your revision decays.
How to read this. Four of six sections went up, and Spring Boot (92%) and Testing (88%)
are now genuinely safe — those are done, stop revising them. Everything that went wrong is in two places:
Data Management at 36% (7 misses — transactions, ACID, JdbcTemplate, derived queries) and
Spring Security at 60% (2 misses). Note also that 4 of the 6 "Spring Core" misses are AOP, which the
report hides inside a section you scored 71% on — AOP is a genuine weak spot masquerading as a decent score.
And the clock, third attempt running. 44m 58s of 2h 10m — you used
35% of your time, faster again (74 → 51 → 45 minutes). That's 45 seconds per question with
85 minutes left on the table, while still losing 5 marks to unticked correct options. This is now the most
stubborn pattern in the whole series.
The single most useful table in this review — it shows which topics stay fixed and which decay.
| Section | #3 · 17 Aug | #4 · 18 Aug | #5 · 19 Aug | Verdict |
|---|---|---|---|---|
| Spring Boot | 45% | 78% | 92% | Fixed — climbed every attempt |
| Testing | 83% | 73% | 88% | Solid |
| Spring Core | 68% | 62% | 71% | Stuck around 70 — AOP is the drag |
| Spring MVC | 60% | 57% | 67% | Improving slowly |
| Spring Security | 33% | 100% | 60% | Volatile — spikes then decays |
| Data Management | 60% | 91% | 36% | Volatile — worst swing in the series |
@annotation(...) pointcut. When an option says "this doesn't work / can't be
done", it is almost always wrong — the exam rarely shows you broken code without saying so.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 |
|---|---|---|---|
| Q5 | Spring AOP statements | "Spring AOP can apply aspects at compile time" | Cross-cutting concerns · can't proxy final methods · JDK proxies by default |
| Q10 | Managing bean scopes | "Use singleton for all beans to simplify configuration" | Use the appropriate scope for the bean's lifecycle and usage |
| Q16 | "A point during execution" | "Advice" | Join point |
| Q21 | AOP advice statements | "proceed() must be called up to 1 time only" | First param of @Around is ProceedingJoinPoint · @AfterThrowing's throwing filters by exception type |
| Q22 | @Transactional properties | "rollback" | rollbackForClassName, noRollbackFor, noRollbackForClassName |
| Q28 | ACID — "as if alone in the system" | "Atomicity" | Isolation |
| Q30 | DataSource for test/dev | "DriverManagerDataSource" | EmbeddedDatabaseBuilder |
| Q35 | Deserialising the request body | "@RequestMapping" | @RequestBody |
| Q36 | @EnableGlobalMethodSecurity | "Configures global settings for securing web endpoints/URLs" | Enables method-level security |
| Q38 | Spring Data comparison keywords | Under-selected — all four were valid | Like, Is, Between, GreaterThan |
| Q39 | @annotation(CustomTransaction) | "Nothing, the pointcut expression is incorrect" | Matches methods annotated with @CustomTransaction |
| Q40 | Supported transaction APIs | Under-selected — missed one of the three | Hibernate, JTA, JPA (+JDBC) — not JDO |
| Q45 | Excluding test auto-configuration | "Use @ContextConfiguration instead of @SpringBootTest" | The exclude attribute of @ImportAutoConfiguration |
| Q46 | JdbcTemplate exception translation | "True" | False — it translates to DataAccessException, not SQLException |
| Q52 | Prototype-scoped beans | Under-selected — missed one of the two | Created on demand each request · created eagerly when injected into a singleton |
| Q53 | Spring Security statements | "permitAll() bypasses Spring Security completely" | Auth data from DB/LDAP/etc · access control at method level |
| Q54 | What derived queries generate | Under-selected — missed one of the three | WHERE conditions · result limit · ordering (never PL/SQL) |
| Q59 | Exposing all Actuator endpoints | Under-selected — missed one of the two | management.endpoints.web.exposure.include=* · management.endpoints.enabled-by-default=true |
91% yesterday, 36% today. This is the section to rebuild end-to-end — and three of the seven are facts from earlier reviews.
rollback attribute — it's rollbackFor and friendsFor.The rollback family is exactly four names — rollbackFor (classes),
rollbackForClassName (strings), noRollbackFor, noRollbackForClassName.
The full attribute list is worth knowing cold, because "which of these is not an attribute" is a stock question:
| Attribute | Takes | Default |
|---|---|---|
propagation | Propagation enum | REQUIRED |
isolation | Isolation enum | DEFAULT (the datastore's) |
timeout | int seconds | -1 (none) |
readOnly | boolean | false |
rollbackFor / rollbackForClassName | classes / strings | — |
noRollbackFor / noRollbackForClassName | classes / strings | — |
transactionManager (alias value) | bean name | the primary one |
The default rule that everything hangs off: unchecked exceptions
(RuntimeException, Error) roll back; checked exceptions do not — you
must name them in rollbackFor. And remember from review #2 that @Transactional
doesn't support SpEL, so these are all fixed types.
Pin the four to a one-word trigger each:
| Property | Trigger words in a question | Guarantee |
|---|---|---|
| Atomicity | "all or nothing", "partial" | every operation commits, or none does |
| Consistency | "valid state", "constraints", "invariants" | rules hold before and after |
| Isolation | "concurrent", "as if alone", "interference" | concurrent transactions don't see each other's work |
| Durability | "crash", "persists", "committed" | committed data survives failure |
Isolation is also the only one you configure in Spring —
@Transactional(isolation = Isolation.REPEATABLE_READ). Know the level/anomaly grid:
| Level | Dirty read | Non-repeatable read | Phantom read |
|---|---|---|---|
READ_UNCOMMITTED | possible | possible | possible |
READ_COMMITTED | prevented | possible | possible |
REPEATABLE_READ | prevented | prevented | possible |
SERIALIZABLE | prevented | prevented | prevented |
EmbeddedDatabaseBuilder — it creates the database; the others just connectnew EmbeddedDatabaseBuilder().setType(H2).addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql").build() launches an in-process H2/HSQLDB/Derby and runs your
scripts — ephemeral, isolated, reset by dropping the bean. That's what makes it the dev/test answer.
| Type | What it is | Use |
|---|---|---|
EmbeddedDatabaseBuilder | stands up an in-memory DB + runs scripts | tests / development |
DriverManagerDataSource | wraps DriverManager.getConnection(), no pool (review #3) | quick scripts against a real DB |
SingleConnectionDataSource | reuses one physical connection | special-purpose adapter |
SmartDataSource | an interface (shouldClose()), not a creation route | implemented by adapters |
Boot ties in automatically: with no configured DataSource and H2/HSQLDB/Derby on the
classpath it auto-creates an embedded DB, seeded via spring.sql.init.schema-locations/data-locations.
Production is still HikariCP.
Like, Is, Between, GreaterThan — every one is validThe keyword vocabulary, which also answers Q54 below:
| Keyword | SQL / JPQL |
|---|---|
Is / Equals / (nothing) | x = ? |
Not | x <> ? |
LessThan / GreaterThan (+Equal) | x < ? / x > ? |
Between | x BETWEEN ? AND ? |
Like / StartingWith / EndingWith / Containing | x LIKE ? / ?% / %? / %?% |
In / NotIn | x IN (?) |
IsNull / IsNotNull | x IS [NOT] NULL |
True / False / IgnoreCase | boolean tests / UPPER(x) = UPPER(?) |
Spring's PlatformTransactionManager abstraction has one implementation per API:
| API | Transaction manager |
|---|---|
| JDBC | DataSourceTransactionManager |
| JPA | JpaTransactionManager |
| Hibernate (native) | HibernateTransactionManager |
| JTA (distributed / global) | JtaTransactionManager |
removed in Spring Framework 5.0 — org.springframework.orm.jdo is gone |
Pair it with review #2's other fact: one resource = local transaction; multiple resources = global/JTA.
DataAccessException, not to a "richer SQLException hierarchy"DataAccessException.The whole point of the translation is to get you away from SQLException:
JdbcTemplate catches the vendor's checked SQLException and rethrows it as an
unchecked DataAccessException, so callers never have to catch or declare it and
your code is independent of JDBC/JPA/Hibernate.
SQLException (checked, vendor-specific error codes)
→ DataAccessException (UNCHECKED — the root)
├── DataIntegrityViolationException
├── DuplicateKeyException
├── EmptyResultDataAccessException
└── IncorrectResultSizeDataAccessException
Watch for this exact bait wording: any option claiming translation into "a more informative
SQLException hierarchy" is false. @Repository is what enables the same translation for
non-JdbcTemplate DAOs.
At startup, PartTree parses the method name into subject +
predicate and compiles it to a JPA CriteriaQuery. Derivable from the name:
| Element | Keyword | Example |
|---|---|---|
| WHERE conditions | By<Property><Op> | findByAgeGreaterThan(int) |
| Logical join | And / Or | findByNameAndActive |
| Limit | First<N> / Top<N> | findTop10ByOrderByScoreDesc |
| Ordering | OrderBy…Asc/Desc | findByStatusOrderByCreatedDesc |
| Distinct | Distinct | findDistinctByLastName |
| Operation | count/exists/deleteBy | countByStatus |
Not derivable: anything vendor-specific — PL/SQL, T-SQL, native functions. Spring
Data is database-agnostic; for vendor SQL you need @Query(nativeQuery = true). And as review #4
noted, a bad property name throws PropertyReferenceException at startup.
Reported inside "Spring Core 71%", but 4 of those 6 misses are AOP — and AOP also cost you 3 marks in attempt #3. It is your most persistent knowledge gap.
Learn the five terms as a sentence: an aspect uses a pointcut to select join points, where it applies advice; the wiring-up is weaving.
| Term | Definition | In code |
|---|---|---|
| Join point | a point during execution — in Spring AOP always a method execution | the matched method call |
| Pointcut | a predicate/expression selecting join points | execution(* svc.*.*(..)) |
| Advice | the action taken at a join point | @Before, @Around, … |
| Aspect | the module combining pointcuts + advice | an @Aspect class |
| Weaving | applying aspects to targets | Spring = runtime proxies only |
| Target / Introduction | the advised object / adding new interfaces | @DeclareParents |
Key contrast the exam repeats: Spring AOP supports only method-execution join points, while AspectJ also supports field access, constructor execution and more.
The three true statements were: AOP handles cross-cutting concerns; Spring AOP
cannot proxy final methods; and JDK dynamic proxies are the default
when the bean implements an interface. The fifth option was also false in a subtle way worth knowing:
the @Aspect/@Before/@Around annotations are AspectJ's
(org.aspectj.lang.annotation) — Spring borrows the annotation style and the pointcut parser, but the
runtime is pure proxy-based Spring AOP with no AspectJ compiler or weaver involved.
| Spring AOP | AspectJ | |
|---|---|---|
| Weaving | runtime proxies only | compile-time, post-compile, load-time |
| Join points | method execution only | methods, constructors, fields… |
| Proxy default | JDK dynamic proxy with an interface; CGLIB without | n/a — bytecode weaving |
| Can advise | public methods on Spring beans | anything, incl. final/private/non-beans |
The proxy limits are a family: final methods/classes can't be overridden,
private methods aren't advised, and self-invocation bypasses the proxy entirely
(the classic reason an internal call to a @Transactional method does nothing).
proceed() may be called any number of times — including zeroThe two true statements: an @Around advice's first parameter must be
ProceedingJoinPoint (it's the only advice type that gets one), and
@AfterThrowing(throwing = "ex") narrows matching to methods that throw that exception
type — declaring SQLException means the advice only fires for SQLException.
The other distractor, @AfterFinally, doesn't exist — the "always runs" advice is
@After (review #3).
What @Around gives you, from review #3's table: it's the only advice
that can (a) call proceed() zero times and skip the method entirely, (b) call it
many times to implement retry, (c) change the arguments via
proceed(Object[] args), and (d) alter or swallow the return value / exception. Its return type
should be Object and it should declare throws Throwable.
@annotation(CustomTransaction)@CustomTransactionLearn the four annotation-matching designators by where the annotation sits — this distinction is the whole question:
| Designator | The annotation must be on… |
|---|---|
@annotation(X) | the method being executed |
@within(X) | the declaring class of the method |
@target(X) | the runtime class of the target object |
@args(X) | the runtime type of an argument |
Two gotchas: the annotation must be @Retention(RUNTIME) or it's invisible and the
pointcut never matches; and with JDK proxies the interface method's annotations are checked, while CGLIB
checks the target class's — so an annotation declared only on the interface may not match under CGLIB.
(Also worth knowing, from the non-annotation family: execution(), within(),
this(), target(), args(), bean().)
Every other option was an absolute — "all beans", "always", "avoid using scopes other than singleton". As review #3 noted, absolutes are where the wrong answer hides. On any "which practice is recommended" question, the balanced, it-depends option is nearly always correct.
The scope table, and when each fits: singleton (default, one per
context) for stateless services and repositories · prototype for stateful, non-shared objects ·
request / session / application / websocket for web-scoped
state — and remember from review #2 that only singleton and prototype work outside a web context.
Four facts, and the exam tests the counter-intuitive middle two:
| Statement | True? | Why |
|---|---|---|
| Created on demand, each time it's requested | yes | a fresh instance per getBean()/injection point |
| Created eagerly when it's a dependency of a singleton | yes | it's injected when the singleton is built — and that same instance is then reused forever |
| Initialised at context bootstrap | no | only singletons are pre-instantiated |
| Container manages its full lifecycle incl. destruction | no | no destruction callbacks — @PreDestroy/DisposableBean are never called; cleanup is yours |
The practical consequence (and a favourite follow-up): injecting a prototype into
a singleton with plain @Autowired gives you one instance for the singleton's whole life,
defeating the point. For a genuinely fresh instance per call use
ObjectProvider<T>.getObject(), @Lookup, or a scoped proxy.
100% yesterday, 60% today — a different corner of the same topic. Both misses are about what a feature does, not how to configure it.
HttpSecurity's job. This annotation switches on method annotations.@EnableGlobalMethodSecurity turns on the annotations you put on individual methods,
and each flag enables a different family:
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true, // @PreAuthorize / @PostAuthorize (SpEL — review #4)
securedEnabled = true, // @Secured("ROLE_ADMIN")
jsr250Enabled = true) // @RolesAllowed (JSR-250)
public class SecurityConfig { }
Two separations to hold: URL security = HttpSecurity /
SecurityFilterChain with request matchers; method security = this annotation +
@PreAuthorize and friends. And in Spring Security 6+ the annotation is renamed
@EnableMethodSecurity (with prePostEnabled on by default) — the exam's Boot 2.5 era
still uses @EnableGlobalMethodSecurity.
permitAll() opens a path — it does not switch security offThe two true statements were: authentication data can come from many backends
(database, LDAP, in-memory, custom — review #3's UserDetailsService fact) and access control
can be configured at the method level. The three false ones are each worth recognising:
permitAll() only whitelists a matcher (the request still passes through the filter chain, still gets
a SecurityContext); Spring Security is not an implementation of the Java EE Security
specification; and no JAAS policy file is required (JAAS integration exists but is optional and
rare).
If you genuinely want a path to skip the chain, that's web.ignoring() /
WebSecurityCustomizer — a different mechanism from permitAll(), and worth knowing
precisely because the exam contrasts them.
92% and 88% — your two strongest sections. Just these two facts left.
exclude attribute of @ImportAutoConfiguration (or a slice's excludeAutoConfiguration)There are three separate exclusion mechanisms, and the exam tests that you know test slices use their own:
| Context | How you exclude |
|---|---|
| Production app | @SpringBootApplication(exclude = …) / @EnableAutoConfiguration(exclude = …) |
| Full-context test | spring.autoconfigure.exclude in properties |
Test slice (@WebMvcTest, @DataJpaTest, …) | @ImportAutoConfiguration(exclude = …), or the slice's own excludeAutoConfiguration attribute |
@WebMvcTest(controllers = OrderController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class)
class OrderControllerTests { … }
The pitfalls are exam-shaped: slices are meta-annotated with @ImportAutoConfiguration,
so exclusions on your application class have no effect on a slice, and
spring.autoconfigure.exclude doesn't apply to slices either. There is no
@DisableAutoConfiguration, and dropping a Maven dependency is a side-effect, not a mechanism.
management.endpoints.web.exposure.include=* — note the webTwo orthogonal switches, which is exactly why this keeps appearing:
| Concern | Property | Default |
|---|---|---|
| Enabled — does the endpoint exist? | management.endpoint.<id>.enabled · globally management.endpoints.enabled-by-default | all enabled except shutdown |
| Exposed — is it reachable over HTTP? | management.endpoints.web.exposure.include / .exclude | only health (and info) |
The invalid keys in this question were management.endpoints.web.enabled-by-default
(no web segment on that one) and management.endpoints.exposure.include (missing
the web segment). Read the key path segment by segment. And note the safety point:
include=* also exposes shutdown if you've enabled it — restrict Actuator in production.
@RequestBody — @RequestMapping only maps the URLReview #3 covered this exact annotation. Keep the four apart by what part of the request each one reads:
| Annotation | Reads | Example |
|---|---|---|
@RequestBody | the body, via an HttpMessageConverter | createUser(@RequestBody User u) |
@RequestParam | query string / form fields | ?page=2 |
@PathVariable | a URI template segment | /users/{id} |
@RequestHeader | a header | Authorization |
@RequestMapping | nothing — it maps the handler to a URL | @RequestMapping("/api") |
The two distractors were a Jackson annotation (@JsonDeserialize, which customises
how a type deserialises, not where the data comes from) and an invented one
(@RequestDeserialize). Combine @RequestBody with @Valid for bean
validation, and remember the body stream is read once.
@ designators, and the Spring-AOP-vs-AspectJ contrast.
(4) Use the clock. You have 85 minutes spare. On every multi-select, read each option and say
"true or false" before moving on — that is 5 marks, more than you need.