Spring Boot Topic
Advanced Spring Boot Interview Questions and Answers
Architecture & Design Patterns
1. How do you implement the Circuit Breaker pattern in Spring Boot?
Answer:
The Circuit Breaker pattern prevents cascading failures in distributed systems.
Using Resilience4j:
@Configuration
public class CircuitBreakerConfig {
@Bean
public CircuitBreakerConfigCustomizer circuitBreakerConfig() {
return CircuitBreakerConfigCustomizer.of("userService", builder ->
builder.slidingWindowSize(10)
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofSeconds(10))
.permittedNumberOfCallsInHalfOpenState(5)
);
}
}
@Service
public class UserService {
@CircuitBreaker(name = "userService", fallbackMethod = "fallbackGetUser")
@RateLimiter(name = "userService")
@Retry(name = "userService", fallbackMethod = "fallbackGetUser")
@Bulkhead(name = "userService")
public User getUser(Long id) {
// External service call
return userClient.getUser(id);
}
public User fallbackGetUser(Long id, Exception ex) {
return new User(id, "Fallback User", "fallback@email.com");
}
}
2. Explain the CQRS pattern implementation in Spring Boot
Answer:
CQRS (Command Query Responsibility Segregation) separates read and write operations.
Implementation:
// Command side (Write)
@Service
@Transactional
public class UserCommandService {
public Long createUser(CreateUserCommand command) {
User user = new User(command.getName(), command.getEmail());
userRepository.save(user);
// Publish domain event
eventPublisher.publishEvent(new UserCreatedEvent(user.getId()));
return user.getId();
}
}
// Query side (Read)
@Service
@Transactional(readOnly = true)
public class UserQueryService {
public UserView getUserView(Long id) {
return userViewRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
public List<UserView> searchUsers(UserSearchCriteria criteria) {
return userViewRepository.findByCriteria(criteria);
}
}
// Projection for read model
@Component
public class UserViewProjection {
@EventListener
public void handleUserCreated(UserCreatedEvent event) {
User user = userRepository.findById(event.getUserId()).orElseThrow();
UserView userView = new UserView(user.getId(), user.getName(), user.getEmail());
userViewRepository.save(userView);
}
}
3. How do you implement Event Sourcing with Spring Boot?
Answer:
Event Sourcing stores state changes as a sequence of events.
Implementation:
@Entity
public class UserAggregate {
@Id
private String id;
@OneToMany(cascade = CascadeType.ALL)
@OrderBy("version")
private List<DomainEvent> events = new ArrayList<>();
private int version = 0;
public void apply(UserCreatedEvent event) {
this.events.add(event);
this.version++;
}
public void apply(UserEmailChangedEvent event) {
this.events.add(event);
this.version++;
}
// Reconstruct aggregate from events
public static UserAggregate recreateFromEvents(List<DomainEvent> events) {
UserAggregate aggregate = new UserAggregate();
events.forEach(aggregate::applyEvent);
return aggregate;
}
}
@Repository
public class EventStoreRepository {
public void save(String aggregateId, DomainEvent event) {
// Store event with version
eventStore.save(event);
}
public List<DomainEvent> getEvents(String aggregateId) {
return eventStore.findByAggregateIdOrderByVersion(aggregateId);
}
}
@Service
public class UserCommandHandler {
public void handle(CreateUserCommand command) {
UserAggregate aggregate = new UserAggregate();
aggregate.apply(new UserCreatedEvent(command.getId(), command.getName()));
eventStore.save(aggregate.getId(), aggregate.getChanges());
}
}
Performance & Scaling
4. How do you implement distributed caching in Spring Boot?
Answer:
Using Redis for distributed caching:
Configuration:
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.disableCachingNullValues()
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.withInitialCacheConfigurations(getCacheConfigurations())
.build();
}
private Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("users", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(5)));
configMap.put("products", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)));
return configMap;
}
}
@Service
public class UserService {
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUser(Long id) {
return userRepository.findById(id).orElse(null);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userRepository.save(user);
}
@CachePut(value = "users", key = "#user.id")
public User saveUser(User user) {
return userRepository.save(user);
}
}
5. How do you handle database read-write splitting?
Answer:
Using multiple data sources for read replicas:
Configuration:
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.write")
public DataSource writeDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.read")
public DataSource readDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource routingDataSource() {
ReadWriteRoutingDataSource routingDataSource = new ReadWriteRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("write", writeDataSource());
dataSourceMap.put("read", readDataSource());
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(writeDataSource());
return routingDataSource;
}
}
// Custom routing data source
public class ReadWriteRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly()
? "read" : "write";
}
}
// Usage
@Service
public class UserService {
@Transactional(readOnly = true) // Uses read replica
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
@Transactional // Uses write master
public User save(User user) {
return userRepository.save(user);
}
}
Security & Advanced Concepts
6. How do you implement OAuth2 Resource Server with JWT in Spring Boot?
Answer:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthenticationConverter())
)
);
return http.build();
}
private Converter<Jwt, AbstractAuthenticationToken> jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
// Extract roles from JWT
Collection<String> roles = jwt.getClaimAsStringList("roles");
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
});
return converter;
}
}
// JWT configuration
@Configuration
public class JwtConfig {
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("https://idp.example.com/.well-known/jwks.json").build();
}
}
7. How do you implement method-level security with custom permissions?
Answer:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomMethodSecurityExpressionHandler expressionHandler =
new CustomMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
// Custom permission evaluator
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication,
Object targetDomainObject,
Object permission) {
if (targetDomainObject instanceof User) {
User user = (User) targetDomainObject;
return hasUserPermission(authentication, user, permission.toString());
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication,
Serializable targetId,
String targetType,
Object permission) {
if ("User".equals(targetType)) {
User user = userRepository.findById((Long) targetId).orElse(null);
return hasUserPermission(authentication, user, permission.toString());
}
return false;
}
private boolean hasUserPermission(Authentication auth, User user, String permission) {
// Custom permission logic
return true;
}
}
// Usage in service
@Service
public class UserService {
@PreAuthorize("hasPermission(#userId, 'User', 'READ')")
public User getUser(Long userId) {
return userRepository.findById(userId).orElse(null);
}
@PreAuthorize("hasRole('ADMIN') or hasPermission(#user, 'WRITE')")
public User updateUser(User user) {
return userRepository.save(user);
}
@PostAuthorize("returnObject.owner == authentication.name")
public User getSensitiveUser(Long id) {
return userRepository.findById(id).orElse(null);
}
}
Testing & Quality
8. How do you implement contract testing with Spring Cloud Contract?
Answer:
Contract testing ensures API compatibility between consumers and providers.
Provider Side (Producer):
// src/test/resources/contracts/shouldReturnUser.groovy
Contract.make {
description "Should return user by id"
request {
method GET()
url("/api/users/1")
}
response {
status OK()
headers {
contentType applicationJson()
}
body([
id: 1,
name: "John Doe",
email: "john@example.com"
])
}
}
// Base test class
@SpringBootTest
public abstract class UserBaseTest {
@Autowired
private UserRepository userRepository;
@BeforeEach
public void setup() {
User user = new User(1L, "John Doe", "john@example.com");
userRepository.save(user);
}
}
Consumer Side:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserConsumerTest {
@Autowired
private UserService userService;
@Test
public void shouldReturnUser() {
// Given
given(userService.getUser(1L))
.willReturn(new User(1L, "John Doe", "john@example.com"));
// When
User user = userService.getUser(1L);
// Then
assertThat(user.getName()).isEqualTo("John Doe");
}
}
9. How do you implement performance testing in Spring Boot?
Answer:
Using JMH (Java Microbenchmark Harness) for microbenchmarks:
JMH Benchmark:
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class UserServiceBenchmark {
@Autowired
private UserService userService;
private ApplicationContext context;
@Setup
public void setup() {
context = SpringApplication.run(Application.class);
userService = context.getBean(UserService.class);
}
@Benchmark
public void benchmarkFindUser() {
userService.findUser(1L);
}
@TearDown
public void tearDown() {
SpringApplication.exit(context);
}
}
Integration Performance Testing:
@SpringBootTest
@ActiveProfiles("perf-test")
public class UserServicePerformanceTest {
@Autowired
private UserService userService;
@Test
public void performanceTestFindUser() {
// Warm up
IntStream.range(0, 1000).forEach(i -> userService.findUser(1L));
// Measure
long startTime = System.currentTimeMillis();
IntStream.range(0, 10000).forEach(i -> userService.findUser(1L));
long endTime = System.currentTimeMillis();
double averageTime = (endTime - startTime) / 10000.0;
assertThat(averageTime).isLessThan(10.0); // Less than 10ms per operation
}
}
Advanced Spring Boot Features
10. How do you implement custom auto-configuration with conditions?
Answer:
@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "my.service", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(MyServiceProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getUrl(), properties.getTimeout());
}
@Bean
@ConditionalOnBean(MyService.class)
@ConditionalOnWebApplication
public MyServiceController myServiceController(MyService myService) {
return new MyServiceController(myService);
}
}
// Configuration properties
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private String url;
private int timeout = 5000;
private boolean enabled = true;
// getters and setters
}
// Register in spring.factories
// META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyServiceAutoConfiguration
11. How do you implement custom health indicators?
Answer:
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Autowired
private ExternalService externalService;
@Override
public Health health() {
try {
boolean isHealthy = externalService.isHealthy();
if (isHealthy) {
return Health.up()
.withDetail("service", "External Service")
.withDetail("responseTime", externalService.getResponseTime())
.build();
} else {
return Health.down()
.withDetail("service", "External Service")
.withDetail("error", "Service unavailable")
.build();
}
} catch (Exception ex) {
return Health.down(ex)
.withDetail("service", "External Service")
.build();
}
}
}
// Database health indicator
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid(1000)) {
return Health.up()
.withDetail("database", "Connected")
.build();
} else {
return Health.down()
.withDetail("database", "Connection invalid")
.build();
}
} catch (SQLException ex) {
return Health.down(ex)
.withDetail("database", "Connection failed")
.build();
}
}
}
Microservices & Cloud Native
12. How do you implement distributed tracing in Spring Boot microservices?
Answer:
Using Spring Cloud Sleuth and Zipkin:
Configuration:
# application.yml
spring:
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411
management:
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
Custom tracing:
@Service
public class OrderService {
private final Tracer tracer;
private final OrderRepository orderRepository;
public OrderService(Tracer tracer, OrderRepository orderRepository) {
this.tracer = tracer;
this.orderRepository = orderRepository;
}
@Transactional
public Order createOrder(CreateOrderRequest request) {
Span orderSpan = tracer.nextSpan().name("create-order").start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(orderSpan)) {
orderSpan.tag("user.id", request.getUserId().toString());
orderSpan.event("order.creation.started");
// Business logic
Order order = new Order(request.getUserId(), request.getItems());
Order savedOrder = orderRepository.save(order);
orderSpan.event("order.creation.completed");
return savedOrder;
} finally {
orderSpan.end();
}
}
}
// Custom trace filter
@Component
public class CustomTraceFilter implements Filter {
private final Tracer tracer;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
Span currentSpan = tracer.currentSpan();
if (currentSpan != null) {
currentSpan.tag("custom.tag", "value");
}
chain.doFilter(request, response);
}
}
13. How do you implement API versioning in Spring Boot?
Answer:
Multiple strategies for API versioning:
1. URL Path Versioning:
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {
@GetMapping
public List<UserV1> getUsers() {
// V1 implementation
}
}
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
@GetMapping
public List<UserV2> getUsers() {
// V2 implementation
}
}
2. Header Versioning:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public ResponseEntity<?> getUsers(@RequestHeader(value = "API-Version", defaultValue = "1") String version) {
if ("2".equals(version)) {
return ResponseEntity.ok(userService.getUsersV2());
} else {
return ResponseEntity.ok(userService.getUsersV1());
}
}
}
3. Content Negotiation:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping(produces = "application/vnd.company.app-v1+json")
public List<UserV1> getUsersV1() {
return userService.getUsersV1();
}
@GetMapping(produces = "application/vnd.company.app-v2+json")
public List<UserV2> getUsersV2() {
return userService.getUsersV2();
}
}
4. Custom Request Mapping:
@RestController
public class VersionedUserController {
@GetMapping(value = "/api/users", headers = "X-API-Version=1")
public List<UserV1> getUsersV1() {
return userService.getUsersV1();
}
@GetMapping(value = "/api/users", headers = "X-API-Version=2")
public List<UserV2> getUsersV2() {
return userService.getUsersV2();
}
}
Data & Transactions
14. How do you handle multi-tenancy in Spring Boot applications?
Answer:
Database-level multi-tenancy with separate schemas:
Configuration:
@Component
public class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static void setCurrentTenant(String tenant) {
currentTenant.set(tenant);
}
public static String getCurrentTenant() {
return currentTenant.get();
}
public static void clear() {
currentTenant.remove();
}
}
public class TenantAwareRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenant();
}
}
@Configuration
public class MultiTenantConfig {
@Bean
@Primary
public DataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
// Add tenant data sources
targetDataSources.put("tenant1", tenant1DataSource());
targetDataSources.put("tenant2", tenant2DataSource());
TenantAwareRoutingDataSource routingDataSource = new TenantAwareRoutingDataSource();
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(tenant1DataSource());
return routingDataSource;
}
}
// Tenant resolver filter
@Component
public class TenantFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String tenantId = httpRequest.getHeader("X-Tenant-ID");
if (tenantId != null) {
TenantContext.setCurrentTenant(tenantId);
}
try {
chain.doFilter(request, response);
} finally {
TenantContext.clear();
}
}
}
15. How do you implement optimistic locking in Spring Data JPA?
Answer:
Using @Version annotation for optimistic locking:
Entity:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer quantity;
@Version
private Long version;
// Constructors, getters, setters
}
@Service
@Transactional
public class ProductService {
@Autowired
private ProductRepository productRepository;
public void updateProductQuantity(Long productId, Integer newQuantity) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new ProductNotFoundException(productId));
product.setQuantity(newQuantity);
try {
productRepository.save(product);
} catch (OptimisticLockingFailureException ex) {
// Handle concurrent modification
throw new ConcurrentModificationException("Product was modified by another transaction");
}
}
@Retryable(value = OptimisticLockingFailureException.class, maxAttempts = 3)
public void updateWithRetry(Long productId, Integer newQuantity) {
updateProductQuantity(productId, newQuantity);
}
}
// Custom exception handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(OptimisticLockingFailureException.class)
public ResponseEntity<ErrorResponse> handleOptimisticLocking(OptimisticLockingFailureException ex) {
ErrorResponse error = new ErrorResponse("CONCURRENT_MODIFICATION",
"The resource was modified by another transaction. Please retry.");
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
}
Advanced Testing
16. How do you test Spring Boot applications with Testcontainers?
Answer:
Using Testcontainers for integration testing with real databases:
Configuration:
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
public class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:13")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void shouldSaveUser() {
User user = new User("John", "john@test.com");
User saved = userRepository.save(user);
assertThat(saved.getId()).isNotNull();
assertThat(saved.getName()).isEqualTo("John");
}
}
// Redis Testcontainers test
@SpringBootTest
@Testcontainers
public class CacheIntegrationTest {
@Container
static RedisContainer redisContainer = new RedisContainer("redis:6-alpine");
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.redis.host", redisContainer::getHost);
registry.add("spring.redis.port", redisContainer::getFirstMappedPort);
}
@Autowired
private UserService userService;
@Test
void shouldCacheUser() {
User user = userService.getUser(1L); // First call - from DB
User cachedUser = userService.getUser(1L); // Second call - from cache
assertThat(cachedUser).isEqualTo(user);
}
}
Performance Optimization
17. How do you implement connection pooling and optimize database performance?
Answer:
Advanced HikariCP configuration and database optimization:
Configuration:
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 20000
max-lifetime: 1200000
leak-detection-threshold: 60000
connection-test-query: SELECT 1
pool-name: MyAppPool
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
order_inserts: true
order_updates: true
batch_versioned_data: true
Optimization techniques:
@Service
@Transactional
public class BatchUserService {
@PersistenceContext
private EntityManager entityManager;
public void saveUsersInBatch(List<User> users) {
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
// Flush and clear session periodically to avoid memory issues
if (i % 50 == 0) {
entityManager.flush();
entityManager.clear();
}
}
}
@Async
@Transactional
public CompletableFuture<Void> processUsersAsync(List<User> users) {
// Process users asynchronously
return CompletableFuture.completedFuture(null);
}
}
// Query optimization with projections
public interface UserProjection {
Long getId();
String getName();
String getEmail();
}
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u.id as id, u.name as name, u.email as email FROM User u WHERE u.active = true")
List<UserProjection> findActiveUsers();
@EntityGraph(attributePaths = {"orders", "profile"})
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdWithAssociations(@Param("id") Long id);
}
Monitoring & Observability
18. How do you implement custom metrics and monitoring in Spring Boot?
Answer:
Using Micrometer for custom metrics:
Custom metrics:
@Component
public class BusinessMetrics {
private final MeterRegistry meterRegistry;
private final Counter userRegistrationCounter;
private final Timer orderProcessingTimer;
private final Gauge activeUsersGauge;
private final DistributionSummary orderValueSummary;
public BusinessMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.userRegistrationCounter = Counter.builder("user.registration")
.description("Number of user registrations")
.tag("type", "success")
.register(meterRegistry);
this.orderProcessingTimer = Timer.builder("order.processing.time")
.description("Order processing time")
.register(meterRegistry);
this.orderValueSummary = DistributionSummary.builder("order.value")
.description("Order value distribution")
.baseUnit("USD")
.register(meterRegistry);
}
public void incrementUserRegistration() {
userRegistrationCounter.increment();
}
public void recordOrderProcessingTime(Runnable task) {
orderProcessingTimer.record(task);
}
public void recordOrderValue(BigDecimal value) {
orderValueSummary.record(value.doubleValue());
}
public void setActiveUsers(int count) {
// Gauges are set directly, not incremented
meterRegistry.gauge("users.active", count);
}
}
// Usage in service
@Service
public class OrderService {
private final BusinessMetrics metrics;
@Transactional
public Order processOrder(Order order) {
return metrics.recordOrderProcessingTime(() -> {
// Process order
Order processedOrder = processOrderInternal(order);
metrics.recordOrderValue(order.getTotalAmount());
return processedOrder;
});
}
}
19. How do you implement graceful shutdown in Spring Boot?
Answer:
@Component
public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {
private static final Logger logger = LoggerFactory.getLogger(GracefulShutdown.class);
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Autowired
private DataSource dataSource;
@Override
public void onApplicationEvent(ContextClosedEvent event) {
logger.info("Starting graceful shutdown...");
// 1. Stop accepting new requests
stopAcceptingNewRequests();
// 2. Wait for ongoing requests to complete
waitForOngoingRequests();
// 3. Close database connections
closeDatabaseConnections();
// 4. Shutdown thread pools
shutdownThreadPools();
logger.info("Graceful shutdown completed");
}
private void stopAcceptingNewRequests() {
// Implementation depends on your setup
// Could involve setting health check to down, etc.
}
private void waitForOngoingRequests() {
try {
Thread.sleep(30000); // Wait 30 seconds for ongoing requests
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void closeDatabaseConnections() {
if (dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close();
}
}
private void shutdownThreadPools() {
taskExecutor.shutdown();
try {
if (!taskExecutor.getThreadPoolExecutor().awaitTermination(60, TimeUnit.SECONDS)) {
taskExecutor.getThreadPoolExecutor().shutdownNow();
}
} catch (InterruptedException e) {
taskExecutor.getThreadPoolExecutor().shutdownNow();
Thread.currentThread().interrupt();
}
}
}
// Configuration
@Configuration
public class GracefulShutdownConfig {
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(connector -> {
// Configure connector for graceful shutdown
});
return tomcat;
}
}
Advanced Security
20. How do you implement attribute-based access control (ABAC) in Spring Boot?
Answer:
@Component
public class AbacPermissionEvaluator implements PermissionEvaluator {
@Autowired
private PolicyEnforcement policyEnforcement;
@Override
public boolean hasPermission(Authentication authentication,
Object targetDomainObject,
Object permission) {
// Build ABAC request
AbacRequest request = AbacRequest.builder()
.subject(getSubject(authentication))
.resource(targetDomainObject)
.action(permission.toString())
.environment(getEnvironment())
.build();
return policyEnforcement.check(request);
}
@Override
public boolean hasPermission(Authentication authentication,
Serializable targetId,
String targetType,
Object permission) {
Object resource = loadResource(targetType, targetId);
return hasPermission(authentication, resource, permission);
}
private Subject getSubject(Authentication authentication) {
User user = (User) authentication.getPrincipal();
return Subject.builder()
.id(user.getId())
.roles(user.getRoles())
.attributes(user.getAttributes())
.build();
}
private Environment getEnvironment() {
return Environment.builder()
.currentTime(LocalDateTime.now())
.ipAddress(getClientIp())
.build();
}
}
@Service
public class PolicyEnforcement {
@Autowired
private PolicyRepository policyRepository;
public boolean check(AbacRequest request) {
List<Policy> policies = policyRepository.findApplicablePolicies(request);
return policies.stream()
.allMatch(policy -> evaluatePolicy(policy, request));
}
private boolean evaluatePolicy(Policy policy, AbacRequest request) {
// Evaluate policy rules against request
return policy.getRules().stream()
.allMatch(rule -> ruleEvaluator.evaluate(rule, request));
}
}
// Usage
@Service
public class DocumentService {
@PreAuthorize("hasPermission(#documentId, 'Document', 'READ')")
public Document getDocument(Long documentId) {
return documentRepository.findById(documentId).orElse(null);
}
@PreAuthorize("hasPermission(#document, 'WRITE')")
public Document updateDocument(Document document) {
return documentRepository.save(document);
}
}
These advanced Spring Boot questions cover complex scenarios that demonstrate deep understanding of the framework, microservices architecture, performance optimization, security, and production-ready features that are essential for senior developer positions.
Comments (0)
No comments yet. Be the first to comment!