Menu

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!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?