You scored 43 / 60 = 72% — +6 marks on attempt #3 and your best yet. Pass is 76% (46/60), so you are +3 questions away. And the story is unambiguous: every section you revised went up — Security 33% → 100%, Data 60% → 91%, Boot 45% → 78%. The two you didn't re-drill went down. The method works; point it at Spring Core and Spring MVC.
How to read this. Four sections are now at or near pass standard. Spring Core is the whole
problem: 8 of your 17 misses, and it's the biggest section on the blueprint, so 62% there caps your total
score no matter what else you do. Spring MVC at 57% is second — only 3 raw marks, but it's the lowest
percentage on the sheet and two of those three were the same @RestController question in
different clothes. Fix Core + MVC and you're through.
And the timing. 51m 21s of 2h 10m — you used 39% of your time, faster still than
last attempt's 1h 15m. That is roughly 51 seconds per question. Five of these 17 were multi-selects where
you left a correct option unticked — the exact failure mode that an unhurried second read fixes. You have
79 minutes of unused time to spend on that.
@Controller, @RestController,
@Repository, @Service) was correct. When a multi-select offers four items from the
same family, "all of them" is very often the answer.Your answer is reconstructed from the exported result — if any line looks wrong to you, tell me and I'll correct it. Click the topic to jump to the full fix.
| Q | Topic | What you answered | The answer |
|---|---|---|---|
| Q10 | Controller return types | Under-selected — missed one of the valid types | void, String, ModelAndView (not HttpServletRequest) |
| Q13 | @WebMvcTest — which is NOT correct | "@WebMvcTest auto-configures MockMvc" | None of the above (all three were true) |
| Q15 | Setting the SpEL compiler mode | "Through a Maven SpEL plugin" | SpelParserConfiguration + the spring.expression.compiler.mode system property |
| Q25 | Adding a bean definition | Under-selected — likely missed registerBeanDefinition | XML <bean/>, @Bean method, DefaultListableBeanFactory.registerBeanDefinition |
| Q27 | JPA query method names | "deleteAgeLessThan(int age)" | findByLastName, countByAgeGreaterThan |
| Q32 | Stereotypes found by scanning | Under-selected — all four were correct | @Controller, @RestController, @Repository, @Service |
| Q33 | Precedence between two advices | "None of the above, we can't control that" | @Order |
| Q37 | SpEL compiled-mode limits | Under-selected — missed one of the four | assignment, custom resolvers/accessors, conversion service, selection/projection |
| Q38 | @RestController returning String | "automatically returns the response in JSON" | @RestController = @Controller + @ResponseBody; @GetMapping maps GET /greeting |
| Q39 | Injecting the Actuator port | "@LocalPort" | @LocalManagementPort and @Value("${local.management.port}") |
| Q40 | Request-processing lifecycle | "returns a view name resolved to a template" | @GetMapping maps the URL; @RestController applies @ResponseBody to all methods |
| Q44 | @Autowired method, unresolvable param | Under-selected — missed one of the three | Exception + method skipped; per-param Optional/@Nullable; method-level required=false |
| Q45 | MockMvc in @SpringBootTest | "Nothing, it's configured by default" | Add @AutoConfigureMockMvc |
| Q46 | Spring/Boot testing facts | "spring-test provides @Mock and @MockBean" | Integration + slice testing supported; @SpringBootTest or @SpringJUnitConfig create the context |
| Q52 | Monitoring method duration | "Use a gauge" | Use a Timer |
| Q53 | @AliasFor | "declares an alias for a bean" | Declares aliases for annotation attributes |
| Q58 | @Transactional supports SpEL? | "True" | False — @Transactional has no SpEL support |
Half your losses, and the section that caps your score. Two clusters here: SpEL internals (Q15, Q37) and container fundamentals you've now missed twice (Q25, Q32, Q33).
SpelParserConfiguration, or the spring.expression.compiler.mode system propertySpelParserConfiguration + the system property.SpEL normally interprets the expression AST on every evaluation. The compiler turns
a hot expression into real bytecode. You switch it on either programmatically —
new SpelParserConfiguration(SpelCompilerMode.MIXED, classLoader) passed to a
SpelExpressionParser — or JVM-wide with
-Dspring.expression.compiler.mode=IMMEDIATE|MIXED|OFF, which sets the default used for framework
expressions (@Value, @Cacheable …). SpEL is a runtime parser, so there is no
build plugin and no JMX surface — both were invented distractors.
The three modes: OFF (default — always interpret) ·
IMMEDIATE (compile on first evaluation; if the runtime types later differ it fails fast and
you must handle it) · MIXED (interpret first, compile in the background after N successful runs, and
silently fall back to interpreting on a type mismatch). The two configuration routes are independent — your own
parser instance always wins for that parser.
Learn it as two short lists.
Not compilable: assignment (name = 'John') · expressions using custom
PropertyAccessor/MethodResolver (runtime dispatch can't be compiled) · anything relying
on the ConversionService for coercion · collection selection
.?[…] and projection .![…].
Compilable (the two you must not tick): ordinary operators
(+ - * / == < >) and constructor calls (new com.example.Foo()).
<bean/> · @Bean method · registerBeanDefinition(...) — and there is no @BeanComponentDefaultListableBeanFactory.registerBeanDefinition.
You correctly avoided the invented @BeanComponent.This is the second time this topic has taken a mark (attempt #3 Q11). Learn the whole list once and it can never cost you again:
| Mechanism | Looks like | Notes |
|---|---|---|
| XML | <bean id="x" class="com.X"/> | read by XmlBeanDefinitionReader |
@Bean method | in a @Configuration class | return value becomes the bean |
| Stereotype + scanning | @Component + @ComponentScan | the @Component defines it; the scan only finds it |
| Programmatic | beanFactory.registerBeanDefinition(name, bd) | the one you keep missing — DefaultListableBeanFactory |
BeanDefinitionRegistryPostProcessor | hook at startup | dynamic registration |
@Import | config class or ImportBeanDefinitionRegistrar | how Boot's own starters register beans |
Invented annotations to reject on sight: @BeanComponent, @BeanDefinition,
@Register. The real stereotypes are only @Component, @Service,
@Repository, @Controller (+ @RestController, @Configuration).
@Controller, @RestController, @Repository, @ServiceEvery stereotype is meta-annotated with @Component, which is exactly
why component scanning finds them:
| Annotation | Layer | Extra behaviour beyond @Component |
|---|---|---|
@Component | generic | the base stereotype |
@Controller | web | enables handler mapping |
@RestController | web | @Controller + @ResponseBody |
@Service | business | none — a semantic marker only |
@Repository | data | exception translation to DataAccessException |
@Configuration | config | also a @Component; CGLIB-proxied for inter-@Bean calls |
@Order (or the Ordered interface) — lower value runs first@Order on the aspects.When two aspects advise the same join point, precedence is undefined unless
you set it. @Order(1) on SecurityAspect and @Order(2) on
LoggingAspect makes security run first (lower = higher priority). @Precedence doesn't
exist; @Primary is about injection, not advice. Note the ordering is between aspects —
two advices of the same type inside one aspect still have undefined order, so split them.
(AspectJ's own @DeclarePrecedence exists but isn't what Spring's exam wants here.)
@Order has now appeared in three consecutive attempts, each time meaning something different. Learn the whole map — this is your single highest-value table:
Where @Order is used | What it controls | Attempt |
|---|---|---|
| On two aspects at the same join point | advice execution order — lower first | #4 Q33 |
| On @Configuration classes | the order configs are processed | #3 Q25 |
On beans injected into a List<T> | position in the list | — |
| On filters / interceptors | position in the chain | — |
What @Order never does: it does not change when the container
instantiates a bean (that's @DependsOn / @Lazy — attempt #2 Q38), and it does
not decide which bean wins injection (that's @Primary / @Qualifier). | ||
@TryInvoke.Spring resolves all parameters of an @Autowired method against the
BeanFactory. What happens when one can't be resolved depends on where you declare the
optionality:
| Declaration | Behaviour when a param is missing |
|---|---|
Plain @Autowired (default) | Exception (NoSuchBeanDefinitionException) and the method is not invoked |
@Autowired(required = false) on the method | No exception; the whole method is skipped |
Optional<T>, @Nullable or required=false on a parameter | Method is still invoked; that param gets null/Optional.empty() |
The exam loves that middle-versus-bottom distinction: method level = skip the call,
parameter level = call it with a hole in it. (Constructors differ — use
Optional<T> parameters for optional constructor dependencies.)
@AliasFor (in org.springframework.core.annotation) makes two attributes of
an annotation interchangeable — set either one and both carry the same value; setting both to different
values is an error. It's how @RequestMapping's value/path, and
@SpringBootTest/@WebMvcTest's value/controllers, work.
public @interface MyAnnotation {
@AliasFor("location") String value() default "";
@AliasFor("value") String location() default "";
}
Bean aliases are a different feature entirely: XML <alias name="a" alias="b"/>,
or extra names in @Bean(name = {"primary", "alias"}).
@Transactional's attributes — propagation, isolation,
timeout, readOnly, rollbackFor, transactionManager — accept
enums, ints, booleans and classes. None of them is SpEL-evaluated. Learn the annotations by which
side of that line they sit on:
| Annotation | SpEL? | Where |
|---|---|---|
@Value | yes | #{...} expressions (and ${...} placeholders) |
@PreAuthorize / @PostAuthorize | yes | the whole security expression language |
@Cacheable / @CacheEvict | yes | key, condition, unless |
@ConditionalOnExpression | yes | Boot auto-config conditions |
@Transactional | no | fixed-type attributes only |
@Scheduled | no | ${...} property placeholders only |
Your lowest percentage (57%). Two of the three were the same @RestController question asked
twice — so one fact is worth two marks here.
void, String, ModelAndView — HttpServletRequest is a parameter, never a return typeValid returns: void (you wrote the response yourself, or 204 No
Content with @ResponseBody) · String (a view name — or the literal body under
@ResponseBody) · ModelAndView (view + model together) · a domain object / collection
(serialised by an HttpMessageConverter) · ResponseEntity<T> (body + status +
headers — the production choice) · also Callable, DeferredResult, Model,
View, HttpEntity.
Not valid: HttpServletRequest / HttpServletResponse — those are
injected into parameters.
String from a @RestController is text/plain — not JSON, not a view nameNail the mapping from return type to content type — it converts two misses into two marks:
| Return type in a @RestController | Content-Type | Converter |
|---|---|---|
String | text/plain | StringHttpMessageConverter |
POJO / List<T> | application/json | MappingJackson2HttpMessageConverter |
ResponseEntity<T> | depends on the body | + custom status & headers |
byte[] | application/octet-stream | raw bytes |
The rest of both questions comes from one sentence: @RestController =
@Controller + @ResponseBody on every method. Therefore: no view resolver is
involved · adding @ResponseBody per method is redundant · the return value is never a view
name · and @EnableWebMvc is not required in Boot (auto-configuration handles it —
in fact adding @EnableWebMvc disables Boot's MVC auto-configuration, a classic own goal).
Also true: @GetMapping("/x") is exactly @RequestMapping(value="/x", method=GET).
The lifecycle to recite (Q40's real subject): request →
DispatcherServlet (front controller) → HandlerMapping (URL → method) →
HandlerAdapter (invokes it, applies @ResponseBody) →
HttpMessageConverter writes the body → ViewResolver only if there's no @ResponseBody.
@RestController skips that last step entirely.
All three claims hold: @WebMvcTest auto-configures the Spring MVC
infrastructure, it is meta-annotated with @ExtendWith(SpringExtension.class),
and it auto-configures MockMvc. Review #3 already flagged that negative stems
("NOT", "incorrect") are catching you — and here it cost a mark you had the knowledge to win.
Technique: on a "which is NOT" question, mark each option true/false in the margin
first, then pick the odd one out. If every option is true, "None of the above" is the answer, not a cop-out.
@WebMvcTest, definitively. Loads: @Controller,
@RestController, @ControllerAdvice, @JsonComponent,
Converter/Formatter, Filter, WebMvcConfigurer, and
MockMvc. Does not load: @Service, @Repository, plain
@Component, full auto-configuration, or any datasource/JPA — so collaborators must be supplied with
@MockBean.
@AutoConfigureMockMvc — @SpringBootTest alone does not give you MockMvc@WebMvcTest, not @SpringBootTest.@SpringBootTest loads the full context but registers no MockMvc bean.
@AutoConfigureMockMvc triggers MockMvcAutoConfiguration, which wires one from the
WebApplicationContext — filters, security, error handling and message converters included.
(There is no MockMvc.setUp() or MockMvc.create(); the manual route is
MockMvcBuilders.webAppContextSetup(wac).build().)
| Setup | Context | How you call the app |
|---|---|---|
@WebMvcTest(Ctrl.class) | MVC slice only | MockMvc — automatic |
@SpringBootTest + @AutoConfigureMockMvc | full context, no server | MockMvc — after you add the annotation |
@SpringBootTest(webEnvironment = RANDOM_PORT) | full context + real server | TestRestTemplate / WebTestClient |
Two exam-worthy details: @AutoConfigureMockMvc(addFilters = false) strips the filter
chain (handy to bypass Spring Security), and mixing RANDOM_PORT with MockMvc is
contradictory — MockMvc never touches the embedded server. For WebFlux it's
@AutoConfigureWebTestClient, since MockMvc is servlet-only.
@SpringBootTest or @SpringJUnitConfigKnow which module owns which annotation — that's the whole question:
| Annotation | Comes from |
|---|---|
@Mock, @InjectMocks, @Spy | Mockito |
@MockBean, @SpyBean | spring-boot-test (not spring-test) |
@SpringBootTest, @WebMvcTest, @DataJpaTest | spring-boot-test-autoconfigure |
@ContextConfiguration, @SpringJUnitConfig, @DirtiesContext | spring-test |
The other two distractors: Mockito is the default mocking framework (EasyMock needs
a manual dependency — not "out of the box"), and spies are fully supported via
@SpyBean. Also remember @SpringJUnitConfig = @ExtendWith(SpringExtension.class)
+ @ContextConfiguration, the plain-Spring way to build a test context.
45% → 78% after review #3. Only two left, and one is a meter type you've now been asked about twice.
@LocalManagementPort — or @Value("${local.management.port}")@LocalServerPort).By default Actuator shares the app's port; set management.server.port=8081 to split
them. Then:
| You want | Use |
|---|---|
| The management (Actuator) port | @LocalManagementPort — preferred — or @Value("${local.management.port}") |
| The application port | @LocalServerPort (or @Value("${local.server.port}")) |
@LocalManagementPort lives in
org.springframework.boot.actuate.autoconfigure.web.server. This matters most in tests using
webEnvironment = RANDOM_PORT, where both ports are assigned dynamically.
@ActuatorPort and a bare @LocalPort are invented.
Timer — it records count and total timeTimer.Attempt #3 asked you about gauges; this one asks about timers. Learn the five meters as one block and both questions become free:
| Meter | Measures | Use for |
|---|---|---|
Counter | monotonically increasing count | requests handled, errors |
Gauge | current value at read time | queue size, active connections |
Timer | duration + count | method / request execution time |
DistributionSummary | distribution of values | payload sizes |
LongTaskTimer | duration of in-progress tasks | long-running jobs |
Register it in code, not in application.yml — custom metrics are programmatic:
registry.timer("order.processing.time").record(() -> { … }), or annotate the method with
@Timed. Timers publish count, totalTime, max and percentiles
through /actuator/metrics.
60% → 91%. One question stood between you and a clean sheet.
By + property expressions joined with And/OrdeleteAgeLessThan(int age) — missing the
By; it must be deleteByAgeLessThan.Spring Data parses the method name as subject (find, read,
get, query, count, exists, delete,
remove) + By + predicate. Both parts are 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 |
Keywords worth recognising: GreaterThan/LessThan, Between,
Like/Containing/StartingWith, In, IsNull,
Not, IgnoreCase, OrderBy…Asc/Desc, Top/First.
Property names must match entity fields — findByLastNameFirstName is parsed as one property
lastNameFirstName and, when it doesn't exist, fails at startup, not at call time.
SpelParserConfiguration or -Dspring.expression.compiler.mode (OFF / IMMEDIATE / MIXED)required=false on the method skips it; @Nullable/Optional on a param injects nulltext/plain body (JSON needs an object); never a view name