@RestController vs @Controller, @RequestMapping vs @GetMapping, @SpringBootApplication vs @EnableAutoConfiguration, active profiles, WAR vs embedded containers, Actuator, listing beans, reading environment properties, debug logging and DevTools.
Published September 25, 2026
These questions mix web basics with operational tooling. For Actuator in particular, interviewers want to hear that you know which endpoints are exposed by default, and how to secure them.
@RestController do?Short answer: It marks a class as a web controller whose methods' return values are written directly to the HTTP response body, serialised to JSON by Jackson, instead of being resolved as view names. It's @Controller + @ResponseBody in one annotation.
@RestController
@RequestMapping("/api/orders")
class OrderController {
private final OrderService service;
OrderController(OrderService service) { this.service = service; }
@GetMapping("/{id}")
OrderDto get(@PathVariable long id) { return service.find(id); } // → JSON
}
Learn it in depth → REST Controllers
@Controller and @RestController?Short answer: A @Controller method's return value is treated as a view name (Thymeleaf, JSP), unless the method is also annotated with @ResponseBody. In a @RestController, every method behaves as if it had @ResponseBody, so it returns data (JSON or XML).
Key points to cover:
@Controller for server-rendered HTML pages, and @RestController for APIs. One application can contain both.@RequestMapping and @GetMapping?Short answer: @RequestMapping is the general mapping annotation. It can match any HTTP method (set it with method = RequestMethod.GET), and it's commonly used at class level for a base path. @GetMapping is a composed shortcut for @RequestMapping(method = GET). Its siblings are @PostMapping, @PutMapping, @PatchMapping and @DeleteMapping.
Key points to cover:
@RequestMapping without method matches all HTTP methods. That's usually unintended on a handler method.@SpringBootApplication and @EnableAutoConfiguration?Short answer: @EnableAutoConfiguration does one thing: it turns on Boot's conditional auto-configuration. @SpringBootApplication includes it, together with @SpringBootConfiguration (a @Configuration) and @ComponentScan. You normally use only @SpringBootApplication, on the main class.
Short answer: Inject the Environment, and call getActiveProfiles(). Use acceptsProfiles(Profiles.of("prod")) to check a condition.
@Component
class StartupInfo implements ApplicationRunner {
private final Environment env;
StartupInfo(Environment env) { this.env = env; }
public void run(ApplicationArguments args) {
log.info("Active profiles: {}", Arrays.toString(env.getActiveProfiles()));
if (env.acceptsProfiles(Profiles.of("prod"))) { /* prod-only checks */ }
}
}
Key points to cover:
getActiveProfiles() returns an empty array, and the default profile applies. getDefaultProfiles() returns ["default"].@Profile on beans to scattering if (profile == …) checks through the code.Short answer: A WAR is deployed into an externally installed servlet container (Tomcat, JBoss/WildFly), which hosts one or more applications. With an embedded container, the server is a library inside your app, packaged as a single executable JAR. The app owns its server, and runs anywhere Java runs.
| WAR on an external server | Embedded (executable JAR) | |
|---|---|---|
| Server install and config | Managed separately by operations | Part of the app, configured with server.* properties |
| Deployment | Copy the WAR into the server | java -jar, or a container image |
| Versioning | Server version shared by all apps on it | Each app pins its own server version |
| Fit | Legacy and shared app servers | Microservices, containers, cloud |
Key points to cover:
<packaging>war</packaging> and extend SpringBootServletInitializer.Short answer: Actuator adds production-ready operational endpoints under /actuator:
health, with liveness and readiness groups for Kubernetes.info.metrics, through Micrometer, exportable to Prometheus and others.env, configprops, beans, mappings, loggers (change log levels at runtime), threaddump and heapdump.Key points to cover:
health is exposed by default. Expose more deliberately, with management.endpoints.web.exposure.include=health,info,prometheus.env, heapdump and configprops can leak secrets. Put management on a separate port, or behind Spring Security.Learn it in depth → Health Checks
Short answer: Add spring-boot-starter-actuator, then configure which endpoints are exposed, and how they're secured:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when-authorized
probes:
enabled: true # /actuator/health/liveness and /readiness
Short answer: Inject the ApplicationContext, and call getBeanDefinitionNames(). Or expose the /actuator/beans endpoint, which also shows each bean's type, scope and dependencies.
@Bean
CommandLineRunner listBeans(ApplicationContext ctx) {
return args -> Arrays.stream(ctx.getBeanDefinitionNames()).sorted().forEach(System.out::println);
}
Short answer: Yes:
Environment, and call getProperty("key"), with an optional default and target type.@Value("${key}").@ConfigurationProperties class, which is the preferred, type-safe option./actuator/env endpoint shows every property source and its values (with sensitive values masked).@ConfigurationProperties(prefix = "payments")
record PaymentProperties(String baseUrl, Duration timeout, int maxRetries) { }
// enable it with @EnableConfigurationProperties(PaymentProperties.class) or @ConfigurationPropertiesScan
Common trap: injecting Environment everywhere, and reading raw strings. Typed configuration properties are validated at startup, can carry @Validated constraints, and show up in IDE auto-completion.
Short answer: Set the log level of the loggers you care about:
logging:
level:
com.shop: DEBUG # your code
org.springframework.web: DEBUG # request mapping details
org.hibernate.SQL: DEBUG # SQL statements
Key points to cover:
--debug enables debug output for core loggers, and prints the auto-configuration report, without turning on debug for everything.logging.level.root=DEBUG works, but floods the logs./actuator/loggers/{name}.spring-boot-devtools dependency?Short answer: To speed up the development loop:
~/.config/spring-boot/.Key points to cover:
optional (Maven) or developmentOnly (Gradle), so it never ships.Q: What does /actuator/health check?
A: It aggregates HealthIndicators: disk space, the database (a validation query), Redis, Kafka, mail and so on. You can add your own by implementing HealthIndicator. Kubernetes probes should use the liveness and readiness groups, so that a slow database doesn't make the pod restart in a loop.
Q: How do you add custom information to /actuator/info?
A: Set info.* properties, enable the build and git info contributors (management.info.build.enabled, plus the build-info goal of the Maven plugin), or implement InfoContributor.
Q: How do you publish a custom metric?
A: Inject MeterRegistry, and use registry.counter("orders.placed", "channel", "web").increment(), or Timers and Gauges. Micrometer exports them to Prometheus, Datadog and other backends.
Q: @RequestParam vs @PathVariable?
A: @PathVariable binds part of the URL path (/orders/{id}), and identifies a resource. @RequestParam binds query-string or form parameters (/orders?status=PAID), and is typically used for filtering, sorting and pagination.