You scored 35 / 60 = 58% — the lowest of the six. That reads like a collapse, and it isn't. Two things happened, and both of them are already solved problems. First: you took six days off. Attempt #5 was 19 Aug; this was 25 Aug, with no revision in between — and every section you didn't revisit fell, exactly as review #5 predicted. Second: at least 11 of the 25 misses are reading and answering errors, not gaps. Three questions asked "which is not…" and you answered the positive form. On eleven single-answer questions you ticked two or three options. The knowledge deficit here is roughly 10 marks. The technique deficit is 15.
Read the first row and the second-to-last row together — that is the whole story of this attempt.
Data Management went 36% → 75% (+39): it is the one section review #5 told you to rebuild, and you
did. Spring Boot went 92% → 40% (−52): it is the section review #5 told you to stop revising
because it was "genuinely safe". Six days later it is your worst topic and accounts for
9 of the 25 misses. Nothing else in this dataset is as clean as that pair.
The clock, for the sixth time. 48m 59s of 2h 10m — 38% of your time, 81 minutes left
unused, 49 seconds per question. You are still answering at roughly triple the pace the exam allows, and this
is the attempt where that finally showed up as reading errors.
Read the last two columns as a controlled experiment: one section was revised, five were not.
| Section | #3 · 17 Aug | #4 · 18 Aug | #5 · 19 Aug | #6 · 25 Aug | Verdict |
|---|---|---|---|---|---|
| Data Management | 60% | 91% | 36% | 75% | Revised after #5 → recovered +39 |
| Testing | 83% | 73% | 88% | 70% | Drifting down without upkeep |
| Spring MVC | 60% | 57% | 67% | 67% | Flat — the only stable section |
| Spring Core | 68% | 62% | 71% | 57% | Never above 71 in six attempts |
| Spring Boot | 45% | 78% | 92% | 40% | Not revised after #5 → lost 52 |
| Spring Security | 33% | 100% | 60% | 33% | 100 → 60 → 33 in a week |
INFO and WARN, two of the most valid. Q18 asked which
mechanism can't render content conditionally — you ticked @PreAuthorize and
@Secured, the two that obviously can. Q36 asked which affirmation is false — you ticked two
true ones. These are not gaps. They are three marks you already knew.windows-version, "Logging Anonymization",
JAAS, ICMP, Object, private, "None of the above",
deleteAgeLessThan. Eight of ten are invented names, wrong layers, or absolutes. That is a
recognisable shape, and it can be filtered without knowing the topic.deleteAgeLessThan(int age). This is the same question as Q27 in attempt #4, and you gave the same
wrong answer. Review #4 contains a table with deleteAgeLessThan in it, marked "no — missing
By", and a drill whose options are the identical four strings. You wrote the fix down seven days ago
and then never re-read it. Q26 is the entire argument of this review compressed into one question.
You have 130 minutes and you use 49. Spend them like this, on every single question, without exception:
windows-version, replaceProdDb,
@AutoConfigureDataSource, ActuatorController, "Logging Anonymization" were all
inventions on this paper. (b) Is it an absolute? — "always", "only", "never", "not possible", "must
explicitly declare every". (c) Is it the wrong layer? — @RestController for rendering,
JAAS for storage, ICMP for endpoints, pom.xml for paths.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 |
|---|---|---|---|
| Q4 | JSR-250 annotations | Under-selected — three of four were correct | JSR-250 · enabled by <context:annotation-config> and <context:component-scan> |
| Q8 | Configuring a Boot test context | "@ComponentScan" + "@TestConfiguration" | Nothing — Boot finds @SpringBootConfiguration by walking up |
| Q9 | Valid @Profile usages | Under-selected — three of four were valid | On @Bean · !production on a class · {"dev","staging"} |
| Q10 | Handler return types a ViewResolver can resolve | "Object" | String · View · ModelAndView (also void) |
| Q11 | Configurable logging aspects | "Logging Anonymization" | Levels, pattern, file/console, rotation, colours, groups |
| Q13 | Which is not a valid log level | "INFO" and "WARN" — answered the inverse | EXCEPTION |
| Q14 | HATEOAS auto-configuration | Under-selected — missed one of the two | Replaces @EnableHypermediaSupport · hypermedia ObjectMapper |
| Q17 | CGLIB proxy visibility | "private" | public · protected · package-private |
| Q18 | Which can't conditionally render content | "@PreAuthorize" and "@Secured" — answered the inverse | @RestController |
| Q20 | What @Profile does | Three wrong options ticked | Registers a bean conditionally on the active profile |
| Q21 | Is /actuator/health exposed via JMX by default? | "No" | Yes — JMX default include is * |
| Q22 | Advice types that accept a JoinPoint | "None of the above" | All four — @Before, @After, @AfterReturning, @AfterThrowing |
| Q26 | Valid repository method names | "deleteAgeLessThan(int age)" | findByLastName · countByAgeGreaterThan |
| Q27 | Customising the Actuator path | "Edit pom.xml" + "rename the endpoint classes" | management.endpoints.web.base-path |
| Q28 | @SpringBootTest on JUnit 5 | Three wrong options ticked | @SpringBootTest alone on the class — no @ExtendWith, no @RunWith |
| Q34 | Actuator endpoints provided out of the box | "windows-version" | health, info, loggers, beans, logfile — all real |
| Q35 | Stopping @DataJpaTest replacing the DataSource | "replaceProdDb" + "@AutoConfigureDataSource" | @AutoConfigureTestDatabase(replace = Replace.NONE) |
| Q36 | Which affirmation is false | Two true statements ticked — answered the inverse | "IoC is another name for DI" — DI is one way to do IoC |
| Q37 | Primary purpose of @SpringBootApplication | "Defines a bean" + "defines security" | Enables component scanning of its package and sub-packages |
| Q47 | EmptyResultDataAccessException | Under-selected — four of five were correct | Everything except "should throw a custom exception" |
| Q49 | Protocols Actuator exposes | "ICMP" | HTTP and JMX |
| Q51 | Overriding default security config | "Disabling defaults is not possible; config is additive" | A SecurityFilterChain bean replaces the defaults |
| Q56 | Metrics tag URL format | "tag:KEY:VALUE" + "tag=KEY=VALUE" | tag=KEY:VALUE |
| Q57 | User-detail storage mechanisms | "JAAS" | Database (JDBC) · LDAP · properties/in-memory |
| Q59 | Named parameters with JdbcTemplate | Three wrong options ticked | JdbcTemplate has no named params — use NamedParameterJdbcTemplate |
92% six days ago, 40% today, and five of the nine are Actuator — the single most repeated topic across all six of your reviews. Actuator is worth roughly 5 marks a paper and is entirely memorisable. Learn the two tables below and this section stops costing you the exam.
Two transports, two independent inclusion lists. This is the table that answers Q21, Q34 and Q49 all at once:
| Transport | Property | Default include | Effect |
|---|---|---|---|
| JMX | management.endpoints.jmx.exposure.include | * | every enabled endpoint is an MBean |
| HTTP | management.endpoints.web.exposure.include | health | only health is reachable over HTTP |
Enabled and exposed are different switches. shutdown is the only
endpoint disabled by default, so even include=* won't publish it until you set
management.endpoint.shutdown.enabled=true. And exposing health is not the same as
showing its detail — management.endpoint.health.show-details defaults to never.
Version note for the exam: this is Boot 2.x behaviour, which is what 2V0-72.22
tests. In Boot 3.x, JMX is off unless spring.jmx.enabled=true.
*). HTTP = health only. Enabled ≠ exposed. Only shutdown is disabled.windows-version does not exist — everything else on that list is reallogfile and beans count even though
they're invisible by default.Know the roster well enough to spot a fake. The ones that turn up in questions:
| Endpoint | Enabled by default | HTTP-exposed by default | Note |
|---|---|---|---|
health | yes | yes | the only one, from Boot 2.1 |
info | yes | no | was exposed in 2.0; populated by InfoContributors |
beans, env, mappings, metrics, loggers, conditions, configprops, threaddump, heapdump, scheduledtasks, caches, httptrace | yes | no | opt in via exposure.include |
logfile | yes | no | 404s unless logging.file.name is set |
shutdown | no | no | the only disabled one |
loggers is the one worth remembering for a second reason: it's read and write
— POST /actuator/loggers/com.example with {"configuredLevel":"DEBUG"} changes a log
level at runtime with no restart. That ties straight into Q11 and Q13 below.
shutdown is disabled; only health is HTTP-exposed. Anything OS-flavoured is invented.management.endpoints.web.base-path — a property, never code and never the build fileEverything about where Actuator lives is a property:
| Property | Default | What it moves |
|---|---|---|
management.endpoints.web.base-path | /actuator | the prefix for all endpoints |
management.endpoints.web.path-mapping.<id> | the endpoint id | renames one endpoint's path |
management.server.port | the app port | moves Actuator to its own port |
management.server.base-path | — | context path when on a separate port |
management.endpoints.web.base-path=/admin
management.endpoints.web.path-mapping.health=status
# → GET /admin/status
There is no ActuatorController class. When an option names a class you have
never seen in the docs, that is eliminator (a) from the protocol.
ping; it has no
concept of an application endpoint. Wrong layer — eliminator (c).Actuator has exactly two exposure technologies, and every Actuator property path names one
of them: management.endpoints.web.* or management.endpoints.jmx.*. If a
property or an option mentions any other transport, it is invented. TCP/IP is the layer HTTP runs
on, not something Actuator speaks.
web and jmx. Nothing else.tag=KEY:VALUE — equals separates the parameter, colon separates the pair= is ambiguous.It is an ordinary query parameter whose value is a colon-joined pair, and you repeat it to AND several filters together:
GET /actuator/metrics/http.server.requests?tag=uri:/api/users&tag=status:200
Two related facts that show up alongside it: GET /actuator/metrics lists the available
metric names, and GET /actuator/metrics/{name} without tags lists that metric's available
tag keys and values — which is how you discover what you can filter on.
?tag=KEY:VALUE — one =, then a :. Repeat the parameter to add filters.Boot's logging surface is a fixed, learnable list. Anything on it is fair game in a question; anything off it is invented:
| Aspect | Property |
|---|---|
| Level (per logger or root) | logging.level.root=INFO, logging.level.org.springframework=DEBUG |
| Output destination | logging.file.name, logging.file.path |
| Pattern | logging.pattern.console, .file, .level, .dateformat |
| Rotation | logging.logback.rollingpolicy.max-file-size, .max-history, .total-size-cap |
| Colour | spring.output.ansi.enabled=always|detect|never |
| Groups | logging.group.sql=… then logging.level.sql=DEBUG |
Two pitfalls that get asked: name the config file logback-spring.xml, not
logback.xml, or Spring never processes <springProfile> and
<springProperty>; and the rollingpolicy.* properties are silently ignored
unless logging.file.name is set, because no rolling appender exists without a file.
EXCEPTION — you ticked INFO and WARNThe ladder, most severe to most verbose, is short enough to recite:
| Level | Exists in | Note |
|---|---|---|
OFF | all | nothing is logged |
FATAL | Log4j2 only | Logback/SLF4J have no FATAL — it maps to ERROR |
ERROR · WARN · INFO · DEBUG · TRACE | all | the five you set with logging.level.* |
Boot's default root level is INFO. Setting a level applies to that logger and
everything below it in the dotted hierarchy. Invented levels to recognise on sight:
EXCEPTION, CRITICAL, SEVERE (that one is java.util.logging,
not SLF4J), VERBOSE, NOTICE.
@EnableHypermediaSupport and configures a hypermedia ObjectMapperAdd spring-boot-starter-hateoas and HypermediaAutoConfiguration gives
you three things:
| Provided | What it means |
|---|---|
Replaces @EnableHypermediaSupport | you never write the annotation yourself |
A customised ObjectMapper | serialises EntityModel/CollectionModel as HAL (the default media type) |
LinkDiscoverers | client-side: find links inside a hypermedia response |
@GetMapping("/orders/{id}")
public EntityModel<Order> getOrder(@PathVariable Long id) {
return EntityModel.of(service.findById(id),
linkTo(methodOn(OrderController.class).getOrder(id)).withSelfRel());
}
The distractors were an invented class (HateoasController — eliminator (a)) and a
wrong layer ("HATEOAS Security" — that's Spring Security's job, eliminator (c)). Both distractors were the
exact shapes the protocol filters.
@SpringBootApplication's job under any reading.The annotation is exactly three annotations in a trench coat:
| Meta-annotation | Effect |
|---|---|
@SpringBootConfiguration (a @Configuration) | the class is a source of @Bean definitions — and the marker Boot's test bootstrapper searches for |
@EnableAutoConfiguration | loads auto-configuration classes conditionally from the classpath |
@ComponentScan | scans the annotated class's package and sub-packages |
Be honest about this question: it is badly worded — auto-configuration is at least as defensible a "primary purpose". The exam's reasoning is that component scanning is the effect that depends on where you put the class, which is why the placement rule matters. The practical lesson isn't the answer, it's the technique: when two options are both arguably right, that is precisely the moment to pick one rather than tick both — and you had 81 minutes to think about it.
The placement rule it's really testing: put the class in the root package. Anything above it is never scanned, and tests that live above it fail with Unable to find a @SpringBootConfiguration (that's Q8).
57% — your lowest Core score in six attempts, and it has never once been above 71%. It's the biggest section on the paper, so it deserves the second-largest share of your time after Actuator.
Two separate facts, and the question tested both:
| Annotation | Spec | Purpose | Spring equivalent |
|---|---|---|---|
@PostConstruct | JSR-250 | callback after DI completes | initMethod / InitializingBean |
@PreDestroy | JSR-250 | callback before destruction | destroyMethod / DisposableBean |
@Resource | JSR-250 | inject by name | @Autowired + @Qualifier |
@Inject | JSR-330 | inject by type | @Autowired |
All of them are processed by CommonAnnotationBeanPostProcessor, and
both XML tags register it: <context:annotation-config/> turns on annotation processing,
and <context:component-scan/> includes everything annotation-config does plus
scanning. That "includes" relationship is why both options were correct — and it's a stock exam fact.
In Java config and Boot they're on automatically. One version trap: JSR-250 left the JDK in Java
11, so on Java 11+ outside Boot you need jakarta.annotation-api on the classpath.
@Bean method, on a @Configuration class, negated, or a list — the only invalid form was exclude=@Profile({exclude="production"}): @Profile has one attribute,
value, and it takes strings.Every legal shape, in one block:
@Profile("dev") // single
@Profile({"dev", "staging"}) // OR — active if either is
@Profile("!production") // NOT
@Profile({"!prod", "!staging"}) // AND of two negations
@Configuration @Profile("dev") // class level: all beans inside
class DevConfig {
@Bean @Profile("embedded") // method level: this bean only
DataSource ds() { … }
}
There is no exclude, no include, no name — negation is done
with ! inside the string. And @Profile is itself just a specialised
@Conditional(ProfileCondition.class), which is why it composes onto anything that produces beans,
including @Component classes and other annotations.
! for NOT, an array for OR. Class level or method level. No exclude attribute.Take the four distractors one at a time, because each is a fact worth owning:
| Claim | Verdict | Why |
|---|---|---|
| Registers a bean conditionally on the active profile | TRUE | the definition |
| "Can only be used at class level" | false | class and @Bean method — Q9 showed both |
"@Profile("dev") activates the dev profile" | false | it consumes the active profiles; it never sets them |
| "The bean is created regardless" | false | then the annotation would do nothing |
"Must be combined with @ComponentScan" | false | works on @Bean methods with no scanning at all |
Activation is always external, and the exam asks how:
spring.profiles.active=dev in properties · --spring.profiles.active=dev on the command
line · SPRING_PROFILES_ACTIVE as an env var · @ActiveProfiles("dev") in tests ·
context.getEnvironment().setActiveProfiles(…) programmatically. If none is set, only beans with
no @Profile plus those matching spring.profiles.default (which is
default) are registered.
public, protected, package-private — never privateThe whole rule is just Java's overriding rules, because CGLIB proxying is subclassing: if a subclass could override it, CGLIB can intercept it.
| Method | CGLIB (subclass) | JDK proxy (interface) | Reason |
|---|---|---|---|
public | yes | yes | overridable; on the interface |
protected | yes | no | visible to subclasses; not on an interface |
package-private | yes (same package) | no | the generated subclass sits in the same package |
private | no | no | static dispatch — not overridable |
final (any visibility) | no | no | cannot be overridden |
static | no | no | not dispatched on an instance |
Three consequences the exam likes: final methods fail silently — no error at startup, the
advice simply never runs; self-invocation bypasses the proxy at every visibility level, because
this.method() never goes through the proxy reference; and CGLIB needs an accessible no-arg
constructor on the target class in Spring 6 / Boot 3.
Pair this with the choice rule: Spring picks a JDK dynamic proxy when the target implements an
interface, CGLIB otherwise — unless you force it with proxyTargetClass=true, which Boot does by
default (spring.aop.proxy-target-class=true).
JoinPoint is available as an optional first parameter to every advice type.
The only special case is @Around, which needs the subinterface:
| Advice | First parameter | Controls execution? |
|---|---|---|
@Before | JoinPoint (optional) | no |
@After | JoinPoint (optional) | no |
@AfterReturning | JoinPoint (optional), then returning | no |
@AfterThrowing | JoinPoint (optional), then throwing | no |
@Around | ProceedingJoinPoint — required, and first | yes — must call proceed() |
@Before("execution(* com.app.service.*.*(..))")
public void log(JoinPoint jp) {
log.info("{} args={}", jp.getSignature().getName(), jp.getArgs());
}
@Around("execution(* com.app.service.*.*(..))")
public Object time(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed(); // may be called 0, 1 or many times
}
The two rules that get tested: JoinPoint must be first if declared at all —
binding parameters from returning/throwing come after it; and
ProceedingJoinPoint extends JoinPoint, so @Around gets
getArgs()/getSignature()/getTarget() too, plus proceed().
The relationship is containment, not equivalence:
| Inversion of Control | Dependency Injection | |
|---|---|---|
| What it is | a broad principle: the framework, not your code, controls creation and flow | one specific technique for realising it |
| Scope | also covers template methods, event callbacks, service locators, contextualised lookup | constructor, setter, field injection |
| Relationship | DI ⊂ IoC — every DI is IoC; not every IoC is DI | |
Spring's container is an IoC container that uses DI as its primary mechanism. The classic phrasing on the exam — "object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis" — is a true description of IoC, taken almost verbatim from the literature, so don't let its length make it look like the odd one out.
88% → 70%. All three misses are annotation mechanics, and two of them are about knowing what isn't needed.
@SpringBootConfiguration@SpringBootTest is meta-annotated with
@BootstrapWith(SpringBootTestContextBootstrapper.class). With no classes attribute, that
bootstrapper uses SpringBootConfigurationFinder to search upwards from the test's own package
until it hits a @SpringBootConfiguration — which @SpringBootApplication is
meta-annotated with. Its @ComponentScan and @EnableAutoConfiguration then apply exactly
as in production, which is why re-declaring them registers beans twice.
| Situation | What you do |
|---|---|
| App class at the root, tests in sub-packages | nothing — discovery just works |
Two candidate @SpringBootConfiguration classes | @SpringBootTest(classes = App.class) |
| Test lives above the app class | move the test, or set classes |
| Add extra beans for one test | a nested @TestConfiguration class |
| Not a Boot app at all | @ContextConfiguration + @ExtendWith(SpringExtension.class) |
Know the two failure messages by name — they are stock answers: "Unable to find a @SpringBootConfiguration" (the walk never found one) and "Found multiple @SpringBootConfiguration annotated classes" (a duplicate on the classpath).
@SpringBootTest alone, on the class — no @ExtendWith, no @RunWith@SpringBootTest" — was not among them. This is the clearest single case of
spreading bets instead of committing.Two independent things were being tested at once, and both distractor axes have to fail:
| Axis | Right | Wrong |
|---|---|---|
| Where | on the class | on a method — @SpringBootTest is @Target(TYPE) |
| With what | nothing — since Boot 2.1 it meta-annotates @ExtendWith(SpringExtension.class) | @RunWith(SpringRunner.class) is JUnit 4 and is silently ignored by Jupiter |
@SpringBootTest // Boot 2.1+ on JUnit 5 — this is the whole thing
class UserServiceIT {
@Autowired UserService service;
@Test void loads() { assertNotNull(service); }
}
"Silently ignored" is the exam's favourite consequence: a JUnit 5 class carrying
@RunWith looks like it runs, but Spring never bootstraps, so every @Autowired field is
null. Note the mirror fact from earlier in this conversation — in plain Spring (no Boot),
@ContextConfiguration does still need @ExtendWith(SpringExtension.class), or
use @SpringJUnitConfig which bundles both.
@AutoConfigureTestDatabase(replace = Replace.NONE)@DataJpaTest is a composed annotation, and one of its parts is
@AutoConfigureTestDatabase. You override the part, not the whole:
Replace value | Behaviour |
|---|---|
ANY (default) | always swap the DataSource for an embedded one (H2 → HSQL → Derby) |
AUTO_CONFIGURED | swap only if the DataSource was itself auto-configured |
NONE | never swap — keep the application's real DataSource |
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE) // e.g. run against Testcontainers Postgres
class UserRepositoryIT { … }
The reason it matters in practice — and the reason it's asked — is that leaving the default with H2
on the test classpath silently masks dialect-specific SQL: the tests pass and production breaks. Remember
too that @DataJpaTest is @Transactional and rolls back each test by default.
33% — back to exactly where it was in attempt #3, having touched 100% in attempt #4. Only three questions, but the section is small, so each one costs a lot of percentage.
@RestController — you ticked the two that obviously canAll three distractors do gate content; @RestController is a stereotype that says
"return the body, not a view name" — it has nothing to do with authorisation at all:
| Mechanism | Where it works | Notes |
|---|---|---|
@PreAuthorize("hasRole('ADMIN')") | method / controller | SpEL; needs method security enabled |
@Secured("ROLE_ADMIN") | method | roles only — no SpEL |
sec:authorize="hasRole('ADMIN')" | Thymeleaf template | from thymeleaf-extras-springsecurity |
<sec:authorize> | JSP tag library | legacy, still examinable |
@RestController | — | @Controller + @ResponseBody. Not security. |
Worth keeping straight alongside it: @PreAuthorize/@PostAuthorize need
prePostEnabled, @Secured needs securedEnabled, and
@RolesAllowed (JSR-250) needs jsr250Enabled — all switches on
@EnableGlobalMethodSecurity (Security 5) or @EnableMethodSecurity (Security 6, where
prePostEnabled is on by default).
SecurityFilterChain bean replaces the defaults — it never adds to themThe mechanism is Boot's standard conditional back-off:
SpringBootWebSecurityConfiguration contributes a default chain only
@ConditionalOnMissingBean(SecurityFilterChain.class). The moment you declare one, Boot's is not
created — so whatever you didn't configure simply isn't there.
| What you want | How | Result |
|---|---|---|
| Custom rules | @Bean SecurityFilterChain | replaces the default chain entirely |
| No security at all | @SpringBootApplication(exclude = SecurityAutoConfiguration.class) | no security infrastructure is created |
| Several rule sets | multiple chains with @Order + securityMatcher | first matching chain wins |
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a
.requestMatchers("/public").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();
}
The subtlety that made the fourth option correct too: excluding
SecurityAutoConfiguration is genuinely how you remove Boot's security wholesale. And note the modern
syntax — Security 6 removed .and() chaining and the no-arg .formLogin(); it's the lambda
DSL or Customizer.withDefaults().
UserDetailsService.Everything hangs off one interface with one method —
UserDetailsService.loadUserByUsername(String) — and the exam tests which implementations ship
in the box:
| Backing store | Implementation | Use |
|---|---|---|
| Relational database | JdbcUserDetailsManager / JdbcDaoImpl | production; default users + authorities tables |
| LDAP | LdapUserDetailsManager | enterprise / Active Directory |
| In-memory / properties | InMemoryUserDetailsManager | tests, demos, prototypes |
| JAAS | JaasAuthenticationProvider | an AuthenticationProvider, not a store |
@Bean
UserDetailsService users() {
return new InMemoryUserDetailsManager(
User.withUsername("admin").password("{noop}secret").roles("ADMIN").build());
}
Two facts that ride along: UserDetailsManager extends
UserDetailsService with create/update/delete, and the {noop} prefix is
DelegatingPasswordEncoder's id — real deployments use {bcrypt}.
36% → 75%. This is the section you rebuilt after review #5, and it worked — the biggest single-attempt gain in the whole series. Keep it: the three remaining misses are small and specific.
By + property expressions joined with And/OrdeleteAgeLessThan(int age) — missing the
By. You ticked this exact option in attempt #4, and review #4 has a table saying so.The parse is mechanical, and every part is mandatory:
| Method name | Valid? | Why |
|---|---|---|
findByLastName(String) | yes | subject + By + one property |
countByAgeGreaterThan(int) | yes | count subject + keyword |
findByLastNameFirstName(…) | no | needs And: findByLastNameAndFirstName |
deleteAgeLessThan(int) | no | missing By: deleteByAgeLessThan |
Subjects: find, read, get, query,
search, stream, count, exists, delete,
remove. Keywords: Is/Equals, Not,
LessThan/GreaterThan, Between,
Like/StartingWith/EndingWith/Containing, In,
IsNull, True/False, IgnoreCase,
OrderBy…Asc/Desc, Top/First.
And the consequence that makes it examinable: property names must match entity fields, so
findByLastNameFirstName is parsed as one property lastNameFirstName and, when it doesn't
exist, fails at application startup — not at call time.
Everything about this exception, in the shape questions ask it:
JdbcTemplate call | 0 rows | 1 row | >1 row |
|---|---|---|---|
queryForObject(…) | EmptyResultDataAccessException | the object | IncorrectResultSizeDataAccessException |
query(…) | empty List | List of 1 | List of n |
queryForList(…) | empty List | List of 1 | List of n |
The hierarchy is the reason it's useful:
EmptyResultDataAccessException → IncorrectResultSizeDataAccessException →
DataAccessException → RuntimeException. Because it's a distinct subclass you can catch
"no rows" precisely, without swallowing connection failures or constraint violations — which is exactly what
option 1 said. All of Spring's data access exceptions are unchecked, and the same hierarchy covers JDBC,
JPA and Hibernate, so you can swap the technology without changing catch blocks.
JdbcTemplate has no named parameters — that's NamedParameterJdbcTemplateRead the snippet's two halves against each other: the SQL uses :name, the parameters
arrive as a Map — and JdbcTemplate understands neither.
JdbcTemplate | NamedParameterJdbcTemplate | |
|---|---|---|
| Placeholder | ? positional | :name named |
| Arguments | varargs / Object[] | Map<String,?> or SqlParameterSource |
update() returns | int — rows affected (never a boolean) | |
// wrong — JdbcTemplate cannot parse :name
jdbcTemplate.update("INSERT INTO users(name) VALUES (:name)", params);
// right
namedParameterJdbcTemplate.update("INSERT INTO users(name) VALUES (:name)",
new MapSqlParameterSource("name", name));
NamedParameterJdbcTemplate wraps a JdbcTemplate internally
(getJdbcOperations() reaches it) and translates named placeholders to positional ones before
execution. Boot auto-configures both when a DataSource exists. The other useful
SqlParameterSource is BeanPropertySqlParameterSource, which binds
:name straight from a POJO's getName().
? ⇒ JdbcTemplate · :name ⇒ NamedParameterJdbcTemplate. update() always returns an int row count.67%, unchanged from attempt #5 — the only section that didn't move in either direction. One miss.
String, View, ModelAndView — Object is not a view contractObject tells the resolver nothing about what it's holding.What the DispatcherServlet does with each return type:
| Return type | Treated as |
|---|---|
String | a logical view name → passed to the ViewResolver |
View | the view itself — no resolution needed |
ModelAndView | model and view (or view name) together |
void | view name inferred from the request URL (RequestToViewNameTranslator) |
Map / Model | model attributes; the view name is inferred as for void |
Object | no view contract — with @ResponseBody it becomes the body instead |
The distinction the exam is really drawing: view resolution vs. message conversion. Without
@ResponseBody, a returned String is a view name; with @ResponseBody
(or on a @RestController), the very same String is written straight to the response body
by an HttpMessageConverter, and no ViewResolver is involved at all. That single
annotation flips the meaning of the return value.
* · HTTP default health only · enabled ≠ exposedshutdown is disabled; logfile needs logging.file.name; windows-version is inventedmanagement.endpoints.web.base-path (all) · path-mapping.<id> (one) · management.server.port (separate port)web and jmx?tag=KEY:VALUE, repeated to AND severallogback-spring.xml@EnableHypermediaSupport + HAL ObjectMapper + LinkDiscoverers@Configuration + @EnableAutoConfiguration + @ComponentScan; put it in the root package@PostConstruct/@PreDestroy/@Resource; component-scan ⊃ annotation-config; @Resource injects by name! for NOT · array for OR · class or method level · no excludespring.profiles.active/SPRING_PROFILES_ACTIVE/@ActiveProfilesJoinPoint first; only @Around takes (and requires) ProceedingJoinPointSpringBootConfigurationFinder walks up for @SpringBootConfiguration@RunWith is JUnit 4 and is silently ignored@AutoConfigureTestDatabase(replace = Replace.NONE); default is ANY@PreAuthorize (SpEL) · @Secured (roles) · sec:authorize — @RestController is not securityAuthenticationProvider, not a storeBy + properties joined by And/Or; bad names fail at startupEmptyResultDataAccessException, >1 ⇒ IncorrectResultSizeDataAccessException; query() never throws? ⇒ JdbcTemplate, :name ⇒ NamedParameterJdbcTemplate; update() returns an intString/View/ModelAndView/void; @ResponseBody switches to message converters| When | What | Time |
|---|---|---|
| Today | This review's 25 facts + every drill on this page | 45 min |
| Today | Lesson 16 · Actuator — the two tables, out loud | 20 min |
| Day 2 | Checklists of reviews #1 and #2 | 20 min |
| Day 2 | Lesson 7 · AOP and Lesson 5 · Profiles — Core has never passed 71% | 40 min |
| Day 3 | Checklists of reviews #3, #4, #5 + Lesson 14 · Security | 40 min |
| Day 4 | All six checklists, fast pass — then the full mock, using at least 100 minutes | 2h 30 |
| Day 5 | Next real attempt, protocol on every question | — |
One rule for the next attempt, above all others: you must still be sitting there at the 90-minute mark. Every failure mode on this paper — polarity errors, ticking three options, stopping short on multi-selects — is what answering in 49 minutes looks like.