Spring Boot interview questions: lessons from production
When you're sitting across from a hiring manager or being quizzed by a senior engineer on the team, Spring Boot interview questions rarely ask for textbook definitions. Instead, they probe whether you've actually debugged a production system, scaled a service, or had to explain why a transaction rolled back at 2 AM. This article walks through the most common Spring Boot interview questions asked by teams building real systems, paired with answers that reflect actual production experience rather than documentation excerpts.
How does auto-configuration work?
Auto-configuration is one of Spring Boot's core selling points, yet many developers treat it as magic. In a production context, you need to understand not just that it works, but why certain beans load and others don't.
Spring Boot uses conditional configuration classes—typically annotated with @Configuration—that activate based on what's on your classpath and what properties you've defined. When your application starts, Spring Boot scans for classes annotated with @ConditionalOnClass, @ConditionalOnProperty, @ConditionalOnMissingBean, and similar annotations. If the conditions match, the configuration class runs and registers beans.
Here's a practical example. Suppose you add the PostgreSQL driver to your Maven dependencies but don't configure a DataSource. Spring Boot's DataSourceAutoConfiguration checks @ConditionalOnClass(Driver.class). If the driver is present and you've provided connection properties (like spring.datasource.url), it creates a DataSource bean automatically. If you later add your own @Bean DataSource, the condition @ConditionalOnMissingBean(DataSource.class) prevents duplication.
@Configuration
@ConditionalOnClass(Driver.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public class AutoConfiguredDataSource {
@Bean
public DataSource dataSource(DataSourceProperties props) {
return DataSourceBuilder.create()
.driverClassName(props.getDriverClassName())
.url(props.getUrl())
.username(props.getUsername())
.password(props.getPassword())
.build();
}
}In production, you'll often need to disable auto-configuration for specific classes. Use spring.autoconfigure.exclude in application.properties or the exclude parameter on @SpringBootApplication. We've excluded Thymeleaf auto-configuration when building REST APIs that never needed templating, saving startup time and reducing memory footprint.
What does @SpringBootApplication do?
This annotation is shorthand for three annotations working together. Understanding what it combines tells you a lot about how Spring Boot structures your application.
@SpringBootApplication is a composite annotation that includes:
- @Configuration — Marks the class as a bean definition source, letting you write
@Beanmethods. - @EnableAutoConfiguration — Tells Spring Boot to start auto-configuration based on classpath and properties.
- @ComponentScan — Scans for
@Component,@Service,@Repository, and@Controllerclasses in the package where your main class sits and all sub-packages.
A common gotcha in production: if you organize your code poorly, component scanning might miss beans. For instance, if your main class is in com.example but you have a utility library in com.other, the scanner won't find it. The solution is explicit configuration or moving the main class higher in the package hierarchy.
@SpringBootApplication
public class PaymentServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PaymentServiceApplication.class, args);
}
}
// Equivalent to writing:
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class PaymentServiceApplication { ... }In microservices, we've also customized SpringApplication before calling run() to set default properties, configure logging profiles, or even disable banner printing in containerized environments.
How do you handle transactions?
Transaction management separates junior and senior developers faster than almost anything else. The @Transactional annotation is straightforward syntactically but dangerous if misunderstood.
Spring creates a proxy around methods annotated with @Transactional. The proxy opens a database transaction before the method runs and commits (or rolls back on exception) after it finishes. This works only when you call the method through Spring—calling it directly within the same class bypasses the proxy.
@Service
public class OrderService {
@Transactional
public Order createOrder(CreateOrderRequest req) {
Order order = new Order();
order.setCustomerId(req.getCustomerId());
orderRepository.save(order);
// This calls the proxy version, not the internal method
assignInventory(order);
return order;
}
@Transactional
public void assignInventory(Order order) {
// Database work here
}
}In production, keep transactions short. Long transactions lock rows and degrade throughput. We've traced slowdowns to methods doing external API calls inside @Transactional blocks. Extract external work outside the transaction boundary.
Another critical lesson: understand rollback behavior. By default, @Transactional rolls back only on unchecked exceptions (RuntimeException and its subclasses). Checked exceptions don't trigger rollback unless you specify rollbackFor. We've had incidents where a caught checked exception silently committed corrupt data.
@Transactional(rollbackFor = Exception.class)
public void processPayment(Payment payment) throws Exception {
// Now any Exception, checked or unchecked, rolls back
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditLog(String action) {
// This runs in a separate transaction
// Failures here don't affect the outer transaction
}Use Propagation.REQUIRES_NEW for side effects like audit logging—if the audit write fails, you don't want it rolling back your business transaction.
How do you secure an API?
In modern production systems, security means OAuth 2.0 resource server setup, JWT tokens, and method-level authorization. Generic answers about password hashing miss the point.
The standard pattern is:
- A client (mobile app, frontend, or another service) obtains a JWT token from an authorization server.
- The client includes the token in the
Authorization: Bearer <token>header. - Your Spring Boot service, configured as a resource server, validates the JWT signature and extracts claims (user ID, scopes, roles).
- Method-level security rules check if the request has the required authorities.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.decoder(jwtDecoder()))
)
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasAuthority("SCOPE_admin:write")
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("https://auth-server/.well-known/jwks.json")
.build();
}
}
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id);
}
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public void deleteOrder(@PathVariable Long id) {
orderService.delete(id);
}
}The key principle: principle of least privilege. Grant scopes and roles only as narrow as needed. A mobile app reading orders doesn't need admin scopes. A background job processing payments needs a specific scope for that operation only.
We've also learned to separate authentication (who are you) from authorization (what can you do). Spring Security handles both, but keeping them conceptually distinct in code makes audits and compliance reviews much cleaner.
Common mistakes
- Calling @Transactional methods internally: Direct calls bypass the proxy. Always inject the service and call through Spring if you need transactional semantics across multiple operations.
- Assuming auto-configuration is complete: It's opinionated but not one-size-fits-all. For databases, caching, or security, you'll often need to layer your own configuration on top.
- Ignoring exception handling in transactions: Checked exceptions don't roll back by default. Explicit
rollbackForprevents silent data corruption. - Over-scoped security authorities: Giving every API endpoint broad admin scopes creates massive blast radius if a token is compromised. Audit your
@PreAuthorizeannotations regularly. - Long transactions: Database locks held during external API calls or file uploads tank performance. Offload that work to async jobs or separate transaction boundaries.
Key takeaways
- Auto-configuration is conditional and composable—understand the conditions so you can override or disable intelligently.
@SpringBootApplicationcombines @Configuration, @EnableAutoConfiguration, and @ComponentScan; know what each does to troubleshoot classpath or scanning issues.- Transactions are proxied; short, focused transactions win; understand rollback behavior and propagation to avoid silent failures.
- Security is OAuth 2.0 resource servers plus method-level authorization; apply least-privilege scoping and keep authentication separate from authorization.
- Production experience means knowing not just how features work, but where they break: proxy limits, transaction deadlock, token expiry, and race conditions.






Comments
Loading comments…