Exam Review #7 · 25 Aug 2026, second sitting · 18 to fix

Seventh attempt — 70%, +7 marks in one day, four short of a pass

You scored 42 / 60 = 70%, up from 35/60 this morning. That is the largest single-day gain in the series, and it happened without any new material — you read review #6 and re-sat. Five of the six sections went up. Spring Security went from 33% to 100% with zero misses. Pass is 76% (46/60), so you need +4 — the closest you have been since attempt #4. Two things now stand between you and the pass, and neither is broad: AOP is 5 of your 18 misses, hidden inside a "Spring Core 65%" that looks unremarkable, and you are still ticking two or three options on single-answer questions — that alone was 8 marks.

70%
This attempt (42/60)
58%
This morning (35/60)
76%
Pass mark (46/60)
+4
Questions to pass
Official result · practice exam

70% — you did not pass this time

76% required to pass · 60/60 answered · 42 correct · time used 40m 10s of 2h 10m
Spring Security 100%▲ 670 misses Testing 86%▲ 161 miss Spring MVC 75%▲ 81 miss Spring Core 65%▲ 89 misses Spring Boot 64%▲ 244 misses Data Management 57%▼ 183 misses

Every section review #6 targeted went up, within hours. Security 33 → 100 (its three misses in the morning paper were the exact material in review #6's Security section), Boot 40 → 64, Testing 70 → 86. There is now no argument left about whether the reviews work.
And the one that fell is the one you stopped rehearsing. Data Management went 75 → 57. It was your best section this morning because review #5 had rebuilt it — six days later it decayed, you rebuilt it, and one attempt after that it is sliding again. You are rotating knowledge in and out, not accumulating it.
The clock, seventh attempt running — and now going the wrong way. 40m 10s of 2h 10m: 31% of your time, 90 minutes unused, 40 seconds per question. You were faster today than yesterday (49 min), which was faster than the day before (45 min). This is the one habit that has never once moved.

The finding that matters most: AOP is 5 of your 18 misses

Q3, Q8, Q23, Q26 and Q39 are all Aspect-Oriented Programming, and the score report buries them inside "Spring Core 65%" — a number that looks merely mediocre. Strip AOP out and the rest of Core is running at roughly 80%. AOP alone is worth more than the four marks you need. It is also the most self-contained topic on the syllabus: five advice types, four annotation designators, one proxy model. Lesson 7 plus the five explanations below is a single evening's work, and it closes the gap on its own.

Section scores, attempts #4 to #7

The last two columns are the same day, four hours apart, with review #6 read in between.

Section#4 · 18 Aug#5 · 19 Aug#6 · 25 Aug am#7 · 25 Aug pmVerdict
Spring Security100%60%33%100%Fixed on demand, twice
Testing73%88%70%86%Reliably strong
Spring MVC57%67%67%75%Best MVC score yet
Spring Core62%71%57%65%Never above 71 — AOP is the drag
Spring Boot78%92%40%64%Recovering, not yet back
Data Management91%36%75%57%Most volatile of all — 4 swings in 4 papers
Overall72%70%58%70%
Seven attempts, and the mechanism is now fully mapped. A review lifts its target section by 16–67 points within hours. Untouched sections drift down. The result is that your overall score has sat between 58% and 72% for eleven days while individual sections swing by 50 points — you keep moving the same total between topics. Data Management has now gone 91 → 36 → 75 → 57 in four papers. The only way out is to stop treating revision as "fix the worst section" and start covering all six every time. Your seven reviews now hold 142 corrected facts.
Your three failure modes — 18 misses, and the split has barely changed
Q17 appeared on this morning's paper too — and you got it wrong a second way Q17 here is character-for-character the same question as Q47 in attempt #6: the same ProductRepository code, the same five options about EmptyResultDataAccessException. This morning you under-selected. Review #6 spelled out the fix — "the only false statement was the opinion one: the method should throw a custom exception instead of returning null". This afternoon you ticked exactly that option. The same question, the written answer in front of you hours earlier, missed in the opposite direction. Reading a review is not the same as rehearsing it. Do the drills, don't just read the prose.

The 18 questions you failed

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.

QTopicWhat you answeredThe answer
Q2Custom stereotype annotationThree wrong options ticked (new scope · transactional · replaces @Service)It registers the class as a bean, plus custom metadata
Q3@annotation pointcut"Triggered even if @Transactional is on a parent class method"Matches only methods directly annotated
Q8Valid cross-cutting concernsUnder-selected — missed one of the threeTransactions · Security · Tracing (not File or Network I/O)
Q10Custom RestTemplate headers"application.properties" + "set them on the RestTemplate instance"Pass an HttpEntity carrying HttpHeaders
Q12Custom Boot banner"Rename application.properties to banner.properties"banner.txt in resources · the Banner interface
Q17EmptyResultDataAccessException"Should throw a custom exception instead of returning null"The other four — same question as #6's Q47
Q23Advice taking (JoinPoint, Exception)"Before" + "After Returning"@AfterThrowing, binding via throwing=
Q24Default Actuator URL"/bootactuator" + "/endpoint/actuator" — both inventedhttp://localhost:8080/actuator
Q26Matching an annotated argument type"args" + "bean" + "within"@args
Q27@PostConstruct / @PreDestroy constraints"Can take parameters"Must return void · must take no parameters
Q33Location of spring.factories"WEB-INF/" + "META-INF/config/"META-INF/spring.factories
Q37Programmatic transaction management"TransactionExecutor" + "@Transactional"TransactionTemplate
Q38Externalising configuration"XML files"YAML · properties · environment variables
Q39Advice that can commit or roll back"@AfterReturning" + "@Before" + "@AfterThrowing"@Around — the only one that wraps the call
Q41@Transactional on a test class"Moving it to testA() gives the same behaviour"Own transaction per method · rolled back · @Commit overrides
Q48Constructor injection"@Autowired is necessary to inject dependencies"Optional since Spring 4.3 for a single constructor
Q51Pooling DataSourcesUnder-selected — missed one of the threeHikariCP · DBCP2 BasicDataSource · C3P0 ComboPooledDataSource
Q53Valid SpEL expressions"(() => 2 * 3)()" — JavaScript, not SpELT() · projection ![] · selection ?[] · method calls
Seven of these eighteen were already in an earlier checklist Q17 EmptyResultDataAccessException — #1, #5, #6 (identical question) · Q26 @args — #5 ("@annotation = method · @within = declaring class · @target = runtime class · @args = argument") · Q23 & Q39 advice types — #5 · Q24 Actuator base path — #6 · Q27 JSR-250 lifecycle — #5, #6 · Q3 @annotation designator — #5. That is 7 marks in documents you already have, against a gap of 4.

Jump to a section

① AOP — 5 misses ② Spring Boot — 4 misses ③ Spring Core — 4 misses ④ Data Management — 3 misses ⑤ Testing — 1 miss ⑥ Spring MVC — 1 miss

① AOP 5 misses

This is the section that decides your next attempt. Five marks, one topic, and the whole topic is three small tables: the five advice types, the pointcut designators, and the proxy model. Learn these five explanations and you have your +4 with a mark to spare.

Q39 · Which advice can begin, then commit or roll back advice types · taught in #5

@Around — the only advice that wraps the call

Your answer: "@AfterReturning", "@Before" and "@AfterThrowing" — three ticks on a single-answer question. Each of those runs at one point in time; the question asked for one method that runs at both ends and chooses between two outcomes.

Think of the five advice types as positions around the target call, and it becomes obvious that only one of them can own both sides:

AdviceRunsSees the outcome?Can replace / skip the call?
@Beforebefore onlynono
@AfterReturningafter normal returnthe return valueno
@AfterThrowingafter a throwthe exceptionno
@Afterafter, always (a finally)no — can't tell which happenedno
@Aroundboth sidesbothyes
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object tx(ProceedingJoinPoint pjp) throws Throwable {
    TransactionStatus status = txManager.getTransaction(definition);
    try {
        Object out = pjp.proceed();     // run the target
        txManager.commit(status);       // normal return → commit
        return out;
    } catch (Throwable t) {
        txManager.rollback(status);     // threw → roll back
        throw t;                        // re-throw, or you hide the failure
    }
}

This is how Spring does it. @Transactional is implemented by TransactionInterceptor, an @Around-style MethodInterceptor — which is also why @Transactional is silently ignored on private methods and why self-invocation bypasses it. Making that connection turns four separate exam facts into one.

Memory hook: Need both ends, or the outcome, or to change the result? @Around. Everything else is a single point in time.
Q23 · Advice with signature (JoinPoint, Exception) advice types · taught in #5

@AfterThrowing — the Exception parameter is the giveaway

Your answer: "Before" and "After Returning"@Before runs when nothing has been thrown yet, and @AfterReturning runs only when nothing was thrown at all.

Read advice questions off the parameter list — each advice type binds a different second parameter, and that mapping is one-to-one:

SignatureAdviceBound by
(JoinPoint)any of the four non-around types
(JoinPoint, Exception ex)@AfterThrowingthrowing = "ex"
(JoinPoint, Object result)@AfterReturningreturning = "result"
(ProceedingJoinPoint pjp)@Aroundrequired, always first
@AfterThrowing(pointcut = "execution(* com.example..*Repository.*(..))",
               throwing = "ex")
public void onFailure(JoinPoint jp, DataAccessException ex) { … }

Three facts that ride with it. The declared type filters: narrow the parameter to DataAccessException and the advice only fires for those — the exception still propagates either way. @AfterThrowing never swallows; only @Around with a try/catch can suppress or substitute an exception. And JoinPoint must be first if declared, with the bound parameter after it.

Memory hook: An Exception parameter ⇒ @AfterThrowing + throwing=. A result parameter ⇒ @AfterReturning + returning=.
Q26 · Matching an argument whose runtime type is annotated designators · taught in #5

@args — the @ prefix means "annotated with", every time

Your answer: "args", "bean" and "within" — three ticks, none of them annotation-aware. You picked the un-prefixed twins of the right answer.

The designators come in pairs, and the @ is the whole distinction: without it you match on types and names, with it you match on annotations. Learn the table as four pairs:

DesignatorMatches onExample
execution(…)a method signature patternexecution(* com.app.service.*.*(..))
within(…)any join point inside a type or packagewithin(com.app.service..*)
@within(Ann)types annotated with Ann (declaring class)@within(org.springframework.stereotype.Service)
args(Type…)the runtime types of the argumentsargs(String, int)
@args(Ann)the argument's runtime class is annotated with Ann@args(com.app.Validated)
@annotation(Ann)the executing method carries Ann@annotation(Transactional)
@target(Ann)the runtime class of the target object carries Ann@target(Repository)
target(Type) / this(Type)target object type / proxy typethis(com.app.Marker)
bean(name)the Spring bean namebean(*Service)

The pitfall the exam likes: @args inspects the runtime class of the argument passed in, not the declared parameter type — so if a subclass is passed and only the superclass carries the annotation, it does not match. bean(…) is Spring-only (not AspectJ) and is the one designator that has nothing to do with types at all.

Memory hook: No @ = match a type or name. With @ = match an annotation. args→@args, within→@within, target→@target.
Q3 · What @annotation(Transactional) matches taught in #5

Only methods directly annotated — not classes, not inherited annotations

Your answer: you ticked "the advice will be triggered even if @Transactional is on a parent class method". Method-level annotations are not inherited in Java, and Spring AOP does not fake it for @annotation.

Three separate boundaries, all of which this question tested at once:

Where the annotation is@annotation(X) matches?What would match
On the executing methodyes
On the classno@within(X) or @target(X)
On a parent class's method (overridden here)noannotate the override, or use @within
On an interface methodnoannotate the implementation

This is worth understanding rather than memorising, because it explains a real Spring behaviour: Java does not inherit method annotations (only @Inherited class annotations are inherited, and even then not through interfaces). It is exactly why Spring's own @Transactional resolution uses AnnotatedElementUtils to search up the hierarchy explicitly rather than relying on the JVM — and why documentation warns that putting @Transactional on an interface method is fragile.

Memory hook: @annotation = this method, right here. Class-level ⇒ @within. Nothing about annotations is inherited for free.
Q8 · Which are genuine cross-cutting concerns

Transactions, Security, Tracing — File and Network I/O are domain work

Your answer: under-selectedyou took two of the three and stopped. You correctly rejected both I/O options, which is the hard part.

The test is one question: would this code look the same in a completely unrelated module? If yes, it's cross-cutting. If it exists because of what this particular class is for, it's domain.

ConcernCross-cutting?Why
Transactionsyesbegin/commit/rollback is identical in every service — the reason @Transactional exists
Securityyesthe same authorisation check on hundreds of methods; Spring Security applies it by method interception
Tracingyesspan start/end and correlation IDs, owned by no layer
Logging · caching · retry · metricsyesthe rest of the canonical list
File I/Onoif a class reads files, that is its job — logging about it would be cross-cutting
Network I/Onosame reasoning; the retry/circuit-breaker around it is cross-cutting

The trap in both wrong options is the same move: naming the subject of a cross-cutting concern rather than the concern. And note the related distinction the exam sometimes tests — shared code is not automatically a cross-cutting concern. A utility method called from everywhere is just code reuse; it becomes cross-cutting only when it must be applied around many unrelated methods.

Memory hook: Transactions · security · logging · caching · tracing · metrics · retry. If it's the module's actual job, it isn't cross-cutting.

② Spring Boot 4 misses

40% → 64% after review #6 — real progress, not yet finished. Three of these four are "where does the file live / what is the path", which is pure recall.

Q24 · The default Actuator URL base path · taught in review #6

http://localhost:8080/actuator

Your answer: "/bootactuator" and "/endpoint/actuator"two invented paths, ticked together, while the real one went unticked. This is the single cheapest mark on the paper and the clearest evidence for the protocol.

Review #6 covered the property; this question just asks for its default value:

PropertyDefaultResult
management.endpoints.web.base-path/actuator/actuator/health
set to /manage/manage/health
management.server.portthe app portmoves Actuator to its own port

And remember the exposure rule that goes with it, because they are often asked together: GET /actuator returns a discovery document listing exposed endpoints — which by default is only health over HTTP. Every other endpoint exists but is invisible until you add it to management.endpoints.web.exposure.include.

Memory hook: /actuator. Nothing prefixed with "boot", nothing nested under "endpoint".
Q33 · Where spring.factories lives

META-INF/spring.factories — at the root of META-INF, no sub-folder

Your answer: "WEB-INF/spring.factories" and "META-INF/config/spring.factories"two ticks, both invented locations. WEB-INF isn't even on the classpath in the sense SpringFactoriesLoader means.

SpringFactoriesLoader scans every JAR on the classpath for exactly META-INF/spring.factories and merges what it finds. Keys are interface names, values are comma-separated implementation names:

# META-INF/spring.factories
org.springframework.context.ApplicationListener=\
com.example.MyListener,\
com.example.OtherListener
KeyRegisters
EnableAutoConfigurationauto-configuration classes (legacy — see below)
ApplicationContextInitializerpre-refresh context customisation
ApplicationListenerlisteners registered before refresh
EnvironmentPostProcessormutating the Environment very early
FailureAnalyzerturning exceptions into readable startup diagnostics

The version fact worth knowing: Boot 2.7 deprecated registering auto-configurations under the EnableAutoConfiguration key, moving them to META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (one class per line) alongside the @AutoConfiguration annotation; Boot 3 removed the legacy key entirely. All the other keys still live in spring.factories. The whole mechanism runs before the ApplicationContext exists, which is why it's a file and not a bean.

Memory hook: META-INF/spring.factories — root of META-INF. Auto-config moved to META-INF/spring/…AutoConfiguration.imports in Boot 2.7+.
Q12 · Creating a custom banner

banner.txt in src/main/resources, or implement Banner

Your answer: you ticked "rename application.properties to banner.properties"an invented filename, and renaming your config file would break the application outright.

Exactly two supported routes, plus the properties that control them:

RouteHow
Static filesrc/main/resources/banner.txt — auto-detected
Programmaticapp.setBanner((env, cls, out) -> out.println("…")) before run()
Move the filespring.banner.location=classpath:my-banner.txt
Turn it offspring.main.banner-mode=off (also console, log)
SpringApplication app = new SpringApplication(MyApp.class);
app.setBanner((environment, sourceClass, out) -> out.println("=== My Application ==="));
app.run(args);

banner.txt supports placeholders — ${spring-boot.version}, ${application.version}, ${application.title} (the last two come from MANIFEST.MF, so they're empty when you run from an IDE) — and ANSI colour codes such as ${AnsiColor.BRIGHT_YELLOW}. There is no spring.banner.text property: you can point at a file, but you can't inline the content.

Memory hook: A file (banner.txt) or a class (Banner). spring.banner.location moves it, spring.main.banner-mode=off kills it.
Q38 · What Boot can externalise configuration from

YAML, properties, environment variables — not XML

Your answer: you ticked "XML files". XML configures beans in legacy Spring; it has never been a source of externalised properties in Boot.

The sources, and the order in which later ones win — the precedence list is itself a stock question:

PrioritySourceExample
highestcommand-line arguments--server.port=8081
OS environment variablesSPRING_DATASOURCE_URL=…
Java system properties-Dserver.port=8081
profile-specific filesapplication-prod.yml
lowestapplication.properties / application.ymlpackaged defaults

Two details that get tested. Relaxed binding is what makes environment variables work: SPRING_DATASOURCE_URL, spring.datasource.url and spring.datasource.URL all bind to the same property, because Boot normalises case, hyphens and underscores. And @Value vs @ConfigurationProperties: the first pulls one value, the second binds a whole prefix to a typed object and supports validation and relaxed binding properly.

Memory hook: properties · YAML · env vars · system properties · command line. XML is bean config, never property config.

③ Spring Core 4 misses

These four plus the five AOP misses are what produced "Core 65%". On their own they'd be about 80% — the container fundamentals are in better shape than the headline suggests.

Q2 · A custom stereotype meta-annotated with @Service

It registers the class as a bean — the extra attribute is just metadata

Your answer: three wrong options ticked — a new scope, transactional behaviour, and "replaces @Service". All three claim the annotation adds behaviour. Meta-annotation inherits behaviour; it never invents it.

Spring resolves annotations transitively, so anything meta-annotated with @Component — directly or through @Service/@Repository/@Controller — is picked up by component scanning:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service                                  // ← this is what makes it a bean
public @interface SpecialService {
    String description() default "Special service layer";
}

@SpecialService(description = "Handles special business logic")
public class SpecialOrderService { }      // registered, singleton, name "specialOrderService"
ClaimVerdictWhy
Registers the bean, description is metadataTRUEmeta-annotation inheritance
Defines a new scopefalsescope comes from @Scope; default stays singleton
Makes it transactionalfalsethat needs @Transactional
Documentation onlyfalseRUNTIME retention + @Service means it's active
Replaces @Servicefalseit uses @Service; both remain available

Two mechanics behind it: @Retention(RUNTIME) is mandatory (Spring reads annotations reflectively at runtime), and this is the same technique Spring itself uses — @RestController is just @Controller + @ResponseBody, @SpringBootApplication is three annotations. You can also use @AliasFor to expose an attribute of the meta-annotation, e.g. letting your annotation set the bean name.

Memory hook: Meta-annotation = inherit, never invent. If it isn't in @Service, your custom stereotype doesn't have it either.
Q27 · Constraints on @PostConstruct / @PreDestroy JSR-250 · taught in #5, #6

void return, no parameters — but any visibility

Your answer: you ticked "can take parameters". They can't: the container has nothing to pass. Note you were right to reject "must be public" — private works fine.

The full contract, which is small enough to memorise outright:

ConstraintRule
Return typemust be void
Parametersmust take none
Visibilityany — public, protected, package-private or private
staticmust not be static
Exceptionsmay throw unchecked; a checked exception is not allowed on @PreDestroy
How manyone per class by convention (inherited ones also run)

Place them in the lifecycle to make the constraints obvious: @PostConstruct runs after all dependency injection is complete but before the bean is handed out, which is precisely why it takes no arguments — everything it needs is already injected into fields. @PreDestroy runs on context close, and never runs for prototype-scoped beans, which Spring does not track after creation.

The three equivalent mechanisms, in the order Spring invokes them: 1. @PostConstruct · 2. InitializingBean.afterPropertiesSet() · 3. @Bean(initMethod = "…"). Destruction mirrors it: @PreDestroyDisposableBean.destroy()destroyMethod.

Memory hook: void, no args, not static, any visibility. Annotation first, then interface, then initMethod — and never for prototypes.
Q48 · Is @Autowired necessary?

No — since Spring 4.3 a class with a single constructor is autowired without it

Your answer: you ticked "@Autowired is necessary to inject dependencies"an absolute, and false on two counts: single-constructor classes need nothing, and @Resource/@Inject are alternatives.

The rule and its boundary:

@Controller
public class MyController {
    private final MyService myService;

    public MyController(MyService myService) {   // only constructor → @Autowired implied
        this.myService = myService;
    }
}
SituationNeed @Autowired?
Exactly one constructorno (Spring 4.3+)
Two or more constructorsyes — mark the one to use
Setter or field injectionyes (or @Resource / @Inject)

The other correct option is worth learning as its own fact — how Spring breaks a tie when several beans match a type: @Qualifier narrows the candidates, then @Primary wins among what remains, then @Priority, and finally Spring matches the injection-point name (the field or constructor-parameter name) against the bean names. Only if none of that resolves it do you get NoUniqueBeanDefinitionException. That last fallback is why a parameter called myService quietly matches a bean named myService.

Memory hook: One constructor ⇒ no annotation needed. Tie-break: @Qualifier → @Primary → @Priority → parameter name → exception.
Q53 · Valid SpEL expressions

T(), projection ![], selection ?[], method calls — the arrow function was JavaScript

Your answer: you ticked (() => 2 * 3)(). SpEL has no lambda or arrow-function syntax at all. Recognising the language a snippet is written in is a valid elimination.

The SpEL operators worth recognising on sight:

SyntaxNameDoes
T(java.util.Date)type referencereach statics: T(java.lang.Math).PI
list.![expr]projectionmap each element → new collection
list.?[pred]selectionfilter the collection
list.^[pred] / list.$[pred]first / last matchsingle element
#this, #rootcontext variablescurrent element / root object
obj?.fieldsafe navigationnull instead of NPE
a ?: bElvisdefault when null
isMember('x')method invocationcalls a method on the root object

The distinction most often tested: #{…} is SpEL, evaluated by SpelExpressionParser; ${…} is a property placeholder, resolved by PropertySourcesPlaceholderConfigurer. They look alike, are processed by different machinery, and @Value("#{T(java.lang.Math).PI}") vs @Value("${server.port}") is the classic pairing. Remember ! = map, ? = filter — the projection/selection pair is the one people mix up.

Memory hook: #{} is SpEL, ${} is a placeholder. ![] maps, ?[] filters, T() reaches statics.

④ Data Management 3 misses

75% → 57% — the only section that fell. It has now swung 91 → 36 → 75 → 57 across four papers, which makes it the single strongest argument for covering every section every time rather than chasing the worst one.

Q17 · EmptyResultDataAccessException identical question in attempt #6 — missed twice

Four of the five statements were true; the false one was the design opinion

Your answer: you ticked "the method should throw a custom exception instead of returning null". This morning you under-selected on this same question; review #6 named this exact option as the only false one. Missed twice, in opposite directions.

The transferable lesson first: when an option says a method should be written differently, it is expressing a preference, not a fact about Spring. The exam asks what the framework does. Returning null after catching the exception is a legitimate design choice, so "should throw instead" cannot be correct.

JdbcTemplate call0 rows1 row>1 row
queryForObject(…)EmptyResultDataAccessExceptionthe objectIncorrectResultSizeDataAccessException
query(…) / queryForList(…)empty List1 elementn elements

Hierarchy: EmptyResultDataAccessExceptionIncorrectResultSizeDataAccessExceptionDataAccessExceptionRuntimeException. Because it is a distinct subclass you can catch "no rows" precisely without swallowing connection failures or constraint violations — and because the whole hierarchy is unchecked and technology-neutral, the same catch blocks work for JDBC, JPA and Hibernate.

Memory hook: queryForObject wants exactly one row: 0 ⇒ EmptyResult…, >1 ⇒ IncorrectResultSize…, query() ⇒ never throws. "Should" options are opinions.
Q37 · Programmatic transaction management

TransactionTemplate

Your answer: "TransactionExecutor" (doesn't exist) and "@Transactional" (declarative, not programmatic). One invented name and one right-answer-to-a-different-question, ticked together.

Spring gives you two transaction styles, and the exam wants you to name the class for each:

StyleMechanismWhen
Declarative@Transactional + an AOP proxyalmost always
ProgrammaticTransactionTemplatefine-grained control, conditional rollback, part of a method
Programmatic, low-levelPlatformTransactionManager + TransactionStatus directlyrarely — you manage commit/rollback by hand
txTemplate.execute(status -> {
    accountDao.debit(from, amount);
    accountDao.credit(to, amount);
    if (somethingIsOff) status.setRollbackOnly();   // conditional rollback
    return null;
});

Keep the type names straight, because they are exactly what gets swapped in distractors: TransactionManager is the empty marker interface; PlatformTransactionManager is the real synchronous one (its implementations are DataSourceTransactionManager, JpaTransactionManager, JtaTransactionManager); ReactiveTransactionManager is the reactive sibling. TransactionTemplate is built on a PlatformTransactionManager and follows the same template-callback pattern as JdbcTemplate. Use TransactionCallbackWithoutResult when there's nothing to return.

Memory hook: Declarative = @Transactional. Programmatic = TransactionTemplate. Anything called "…Executor" is invented.
Q51 · Which DataSources actually pool connections

HikariCP, DBCP2 BasicDataSource, C3P0 ComboPooledDataSource

Your answer: under-selectedyou took two of the three real pools. You correctly rejected both traps: DatabaseConnectionFactory (invented) and SingleConnectionDataSource (one connection is the opposite of a pool).

Split the implementations into pools and non-pools, because that's the only distinction the question ever draws:

ImplementationPooledUse
HikariDataSource (HikariCP)yesBoot's default since 2.0
org.apache.tomcat.jdbc.pool.DataSourceyeslegacy Tomcat deployments
BasicDataSource (DBCP2)yesCommons DBCP environments
ComboPooledDataSource (C3P0)yeslegacy C3P0 setups
DriverManagerDataSourcenoopens a fresh connection every call — tests only
SingleConnectionDataSourcenoone physical connection, reused
SimpleDriverDataSourcenouses a Driver instance directly
EmbeddedDatabaseBuildern/acreates an in-memory DB (review #5)

Boot's auto-configuration picks the first pool it finds on the classpath, in this order: HikariCP → Tomcat JDBC → Commons DBCP2 → Oracle UCP. Override with spring.datasource.type. The reason it matters — and the reason the "not a pool" options are traps — is that DriverManagerDataSource pays a full JDBC handshake per request and SingleConnectionDataSource serialises every thread onto one connection.

Memory hook: Hikari · Tomcat · DBCP2 · C3P0 pool. DriverManager · SingleConnection · SimpleDriver do not. Boot picks Hikari first.

⑤ Testing 1 miss

86%, your second-best testing score. One miss, and it's about transaction scope rather than test wiring.

Q41 · @Transactional on a test class

Class level applies to every method — moving it to one method is not equivalent

Your answer: you ticked "@Transactional can be placed on testA() instead and the class logic will be the same". testB() would then run with no transaction and no rollback — a real behavioural difference.

What Spring's test support actually does with @Transactional:

BehaviourDetail
Scopea new transaction per test method, not one for the class
Default outcomerolled back after every method — this is the opposite of production
Commit instead@Commit or @Rollback(false), at class or method level
Run code outside the transaction@BeforeTransaction / @AfterTransaction
Class vs methodmethod-level annotations override the class-level default

The rollback default is the fact to hold on to — it exists so tests don't leak state into one another, and it's why @DataJpaTest is transactional by default too. Two consequences worth knowing: a rolled-back test never exercises the real commit, so constraint violations that only surface at flush time can hide (use @Commit or an explicit flush()); and code that starts its own thread — or runs with REQUIRES_NEW — is outside the test transaction and will not be rolled back.

Memory hook: Class-level = every method, each in its own transaction, all rolled back. @Commit flips it. Method annotations win.

⑥ Spring MVC 1 miss

75% — your best MVC result in seven attempts.

Q10 · Custom headers on a RestTemplate request

Pass an HttpEntity holding HttpHeaders

Your answer: "application.properties" and "set them on the RestTemplate instance". The second is the more interesting error — RestTemplate is a shared, thread-safe bean, so per-request state can't live on it.

Per request you wrap headers in an HttpEntity and use exchange(); globally you add an interceptor:

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(token);
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<Void> entity = new HttpEntity<>(headers);
ResponseEntity<User> res = restTemplate.exchange(
        "/users/{id}", HttpMethod.GET, entity, User.class, 1);
ScopeMechanism
One requestHttpEntity<T> (headers, or headers + body) passed to exchange() / postForObject()
Every requesta ClientHttpRequestInterceptor added to the RestTemplate
Build-time defaultsRestTemplateBuilder.defaultHeader(…) in Boot

The distractor worth pinning down: @RequestHeader is server-side — it reads an incoming header in a controller method. Client-side sending and server-side reading are opposite directions, and the exam pairs them deliberately. Note also that HttpEntity is the request-side type; ResponseEntity extends it and adds the status code.

Memory hook: Sending headers ⇒ HttpEntity (or an interceptor for all requests). Reading them ⇒ @RequestHeader, in a controller.

Your close-the-gap checklist — attempt #7

The 18 facts, one line each
Do this next — you need 4, and AOP alone is 5 (1) Spend one evening on AOP and nothing else. Lesson 7 plus the five explanations above. Five advice types, the designator pairs (args/@args, within/@within, target/@target), and the proxy model. That single topic is bigger than your gap. (2) Do the drills, don't just read. Q17 proves the point: you read review #6's answer this morning and still missed the same question this afternoon. Every review page has interactive drills — clicking through them is what converts reading into recall. (3) Rotate all six sections, not the worst one. Data Management has swung 91 → 36 → 75 → 57 because it only ever gets attention when it's the problem. Your seven checklists are 142 facts; a full pass takes about 45 minutes. (4) The clock — this is now the last unfixed habit. 45 → 49 → 40 minutes across three papers, out of 130. Eight of today's eighteen misses were multiple ticks on single-answer questions, and Q24 had you choosing two invented URLs over the real one. You are not short of knowledge. You are short of forty minutes of care.

The two habits that close a 4-mark gap

  1. Decide the answer count before you read the options. "Which is…" ⇒ tick one. "Which are…" ⇒ tick two or more. Today that one habit was worth 8 marks (Q2, Q10, Q23, Q24, Q26, Q33, Q37, Q39).
  2. Ask "does this name actually exist?" before ticking. Today's inventions: /bootactuator, /endpoint/actuator, banner.properties, META-INF/boot, META-INF/config, TransactionExecutor, DatabaseConnectionFactory. Seven fakes on one paper — the exam relies on you not checking.

And the target for the next sitting: still be in the chair at the 90-minute mark. You have used 40, 49 and 45 minutes on your last three papers out of 130 available. There is no version of this exam where finishing in a third of the time helps you.

I'm your teacher — ask me anything. The highest-value thing you can ask for right now is "drill me on AOP" — five advice types, the pointcut designators, proxies and self-invocation, in exam wording. Or ask for the merged rapid-fire round over all 142 misses from the seven attempts. Say "quiz me on the 18" for this attempt only.
← Dashboard ← Review #6 Lesson 7 · AOP Mock Exam