Senior Java & Spring Boot Interview Questions – Top 20 (Part 7)


Senior Java interviews increasingly focus on more than Java syntax and Spring Boot annotations. Interviewers want to know how you secure applications, coordinate distributed transactions, process millions of records, build reliable event-driven systems, and integrate AI into production applications.

Consider a few real-world situations:

  • A user logs out, but their JWT remains valid.
  • A batch job crashes after processing thousands of records.
  • A database transaction succeeds, but its corresponding Kafka event is never published.
  • An AI application produces a confident answer that is completely incorrect.
  • An API responds in milliseconds, but the complete user journey takes several seconds.

These problems require a deep understanding of application behavior, system architecture, and failure handling.

In this guide, we explore 20 advanced Java and Spring Boot interview questions with detailed answers, code examples, architectural considerations, and production troubleshooting techniques.

Section 1: Spring Security and Application Security

1. How does Spring Security handle method-level authorization, and what is the difference between @PreAuthorize and @PostAuthorize?

Spring Security supports method-level authorization, allowing an application to enforce access-control rules when methods are invoked. This is useful when authorization must be enforced at the service layer rather than relying exclusively on controller-level checks.

Enable method security in a modern Spring Security application using:

@Configuration
@EnableMethodSecurity
public class SecurityConfiguration {
}

@PreAuthorize checks an authorization expression before the method executes.

@Service
public class ReportService {

    @PreAuthorize("hasRole('ADMIN')")
    public Report generateReport() {
        return new Report();
    }
}

Only callers with the required role can invoke the method successfully through the configured Spring Security method interceptor.

You can also use expressions involving method arguments and the current authentication.

@PreAuthorize("#userId == authentication.name")
public UserProfile getProfile(String userId) {
    return userRepository.findProfile(userId);
}

This example assumes that the authenticated principal's name matches the application's user identifier. Real applications must verify that the identity representation is correct and that the rule covers the intended access policy.

@PostAuthorize evaluates an authorization expression after the method executes, making the returned result available through the returnObject variable.

@PostAuthorize(
    "returnObject.ownerId == authentication.name"
)
public Document getDocument(Long documentId) {
    return documentRepository.findById(documentId)
        .orElseThrow();
}

This can help enforce ownership rules when the authorization decision depends on the returned object. However, the method has already executed before the authorization check occurs. Avoid using it when executing the method itself could cause unauthorized side effects.

Aspect @PreAuthorize @PostAuthorize
When it executes Before the method body After the method body
Primary purpose Check permission before execution Check permission using the returned result
Typical use Role, permission, or argument-based checks Ownership checks based on the returned object
Important limitation Must correctly model the required permission Cannot prevent side effects that already occurred

Senior-level takeaway: Use authorization at the appropriate layer. Method-level security complements request-level security, but it does not eliminate the need for carefully designed data-access rules.

2. Why might a JWT remain valid after a user logs out, and how would you handle token revocation in a stateless architecture?

A JSON Web Token (JWT) is commonly validated using its signature and claims, such as its expiration time. In a stateless architecture, the server does not necessarily maintain a session record for every token.

As a result, logging out on the client does not automatically invalidate an already issued access token. The token may remain usable until it expires, provided it continues to pass validation and no additional revocation mechanism is enforced.

Consider this timeline:

10:00 - Access token issued
10:05 - User logs out
10:06 - Previously issued token is presented again
10:30 - Token expires

Without a revocation mechanism, the token may remain valid until 10:30.

Possible solutions

1. Use short-lived access tokens.

Keep access-token lifetimes relatively short and use refresh tokens to obtain new access tokens when appropriate. This limits the period during which a stolen access token can be reused.

2. Maintain a token denylist.

Store identifiers such as a JWT's jti claim in a revocation store until the token expires. Each request must check whether the token has been revoked. This adds a stateful lookup to the authorization process.

3. Revoke refresh tokens.

At logout, revoke the refresh token or token family so that new access tokens cannot be obtained through that refresh credential. This does not automatically invalidate an already issued access token.

4. Use a token version or account security state.

An application can associate tokens with a user-level token version or security timestamp and reject tokens that predate the current value. This requires checking the relevant state when validating a request.

5. Use an identity provider's revocation mechanisms.

When using an external identity provider, use its supported token and session revocation capabilities where applicable.

Important trade-off: Immediate revocation usually requires server-side state, a lookup, or a short-lived token strategy. A completely stateless access-token design cannot guarantee immediate revocation without some additional mechanism.

Senior-level takeaway: Logout, refresh-token revocation, and access-token revocation are related but separate operations. Design them according to the application's security requirements.

3. How would you prevent Broken Access Control when users can access resources belonging to other users?

Broken Access Control occurs when an application allows users to access operations or resources outside their permitted scope.

A common example is an API that accepts a customer ID from the request:

GET /api/customers/1001
GET /api/customers/1002

If user A can change the identifier from 1001 to 1002 and retrieve user B's information, the application has an authorization vulnerability.

This is often called insecure direct object reference (IDOR) or a related object-level authorization failure.

Why authentication alone is insufficient

Authentication establishes who the caller is. Authorization determines what that caller may access. A valid JWT does not automatically grant permission to retrieve every resource whose identifier appears in a URL.

Approach 1: Enforce ownership in the data-access query

@Service
public class OrderService {

    private final OrderRepository repository;

    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }

    public Order getOrder(
            Long orderId,
            String authenticatedUserId) {

        return repository
            .findByIdAndCustomerId(
                orderId,
                authenticatedUserId
            )
            .orElseThrow(
                () -> new OrderNotFoundException()
            );
    }
}

The repository query should constrain both the resource ID and the authenticated user's permitted scope.

Optional<Order> findByIdAndCustomerId(
    Long orderId,
    String customerId
);

The user identifier must come from a trusted authentication context, not from a request parameter that the caller can freely manipulate.

Approach 2: Enforce authorization at multiple layers

  • Authenticate the request.
  • Check role- or permission-based access.
  • Enforce ownership or tenant restrictions.
  • Apply authorization to reads, updates, deletes, exports, and background operations.
  • Test attempts to access resources belonging to other users.

For sensitive resources, consider whether returning a not-found response rather than a forbidden response is appropriate to avoid revealing the existence of another user's resource.

Senior-level takeaway: Never assume that knowing a resource ID proves permission to access it. Enforce authorization against the actual resource and its security context.

4. How does Spring Security handle CSRF, and why might disabling CSRF be unsafe for a browser-based application?

Cross-Site Request Forgery (CSRF) occurs when a browser is tricked into sending an authenticated request that the user did not intend to make.

For example, suppose a banking application authenticates users using a session cookie. A malicious website may attempt to cause the browser to submit a transfer request to the banking application. Because browsers automatically attach eligible cookies, the request may carry the user's session credentials.

Spring Security provides CSRF protection mechanisms that validate an expected token for applicable requests.

A simplified flow looks like this:

Browser requests application
          |
          v
Application provides CSRF token
          |
          v
Browser submits state-changing request
with expected CSRF token
          |
          v
Server validates the token
          |
          +-- Valid: continue processing
          |
          +-- Invalid: reject request

Why disabling CSRF can be dangerous

Disabling CSRF protection for a browser-based application that authenticates through automatically attached cookies can expose state-changing operations to cross-site request forgery.

For example, a cookie-authenticated application might be vulnerable if CSRF protection is disabled and a malicious page can trigger a transfer or account-change request.

When might disabling CSRF be appropriate?

Some applications expose APIs that authenticate exclusively through explicitly supplied bearer tokens rather than ambient browser credentials. In that architecture, traditional CSRF exposure may be significantly different.

However, the decision must account for the actual authentication mechanism, browser behavior, cookie settings, and any endpoints that use session or cookie authentication. Merely calling an endpoint a REST API does not make it immune to CSRF.

Senior-level takeaway: Understand how credentials are sent by the browser before changing CSRF settings. Security configuration should reflect the real authentication model, not a blanket assumption that every API is stateless.

5. How would you implement multi-tenant security in Spring Boot so that one tenant cannot access another tenant's data?

In a multi-tenant application, multiple customers share an application or infrastructure while their data must remain isolated.

For example:

Tenant A
  |
  +-- Customers
  +-- Orders
  +-- Invoices

Tenant B
  |
  +-- Customers
  +-- Orders
  +-- Invoices

A serious vulnerability occurs if a request authenticated for Tenant A can access Tenant B's records by modifying an ID or supplying another tenant identifier.

1. Resolve the tenant from a trusted context

The application must establish the tenant identity through a trusted authentication claim, a validated tenant membership, or another controlled mechanism. Do not trust an arbitrary tenant ID supplied in a request without checking the caller's membership and permissions.

2. Enforce tenant scope in data access

public interface InvoiceRepository
        extends JpaRepository<Invoice, Long> {

    Optional<Invoice> findByIdAndTenantId(
        Long invoiceId,
        String tenantId
    );
}

Every relevant data operation must apply the tenant boundary. A single repository method that forgets the tenant predicate can expose data even when most other queries are correct.

3. Choose an isolation strategy

  • Shared database and shared schema: Rows include a tenant identifier, and every relevant query enforces tenant scope.
  • Separate schema per tenant: Tenants use different schemas, which require careful schema selection and migration management.
  • Separate database per tenant: Provides stronger infrastructure separation but increases operational complexity.

4. Protect background jobs and caches

Tenant isolation must also apply to scheduled tasks, Kafka consumers, exports, search indexes, cache keys, and asynchronous processing.

For example, a cache key should include tenant identity when the cached value is tenant-specific:

String cacheKey =
    tenantId + ":invoice:" + invoiceId;

Otherwise, a value cached for one tenant might accidentally be returned to another.

5. Test tenant boundaries explicitly

Create tests that attempt cross-tenant reads, updates, deletes, exports, and cache access. Database constraints, row-level security, or separate database credentials can provide additional protection depending on the architecture.

Senior-level takeaway: Multi-tenancy is a system-wide isolation requirement, not simply a tenant ID field added to a table.

Section 2: Spring Boot Internals and Asynchronous Processing

6. How does Spring Boot manage bean initialization, and what problems can arise when one bean depends on another during startup?

Spring manages objects known as beans through its application context. During startup, it reads configuration, registers bean definitions, resolves dependencies, creates bean instances as needed, injects dependencies, and invokes lifecycle callbacks.

A simplified lifecycle for a typical singleton bean is:

Bean definition registration
          |
          v
Bean instantiation
          |
          v
Dependency injection
          |
          v
Aware callbacks
          |
          v
BeanPostProcessor before initialization
          |
          v
Initialization callbacks
          |
          v
BeanPostProcessor after initialization
          |
          v
Bean ready for use

The actual lifecycle contains additional details, including proxy creation and special handling for some bean types.

What can go wrong?

Circular dependencies: Bean A depends on Bean B, while Bean B also depends on Bean A. Constructor-based circular dependencies generally cannot be resolved by ordinary dependency injection.

Premature access: A bean may call another bean before the latter has completed its initialization.

External dependency failures: Initialization code may attempt to connect to a database, broker, or remote API that is unavailable.

Heavy initialization: Expensive work performed during bean creation can increase startup time or make application startup fragile.

Example of a circular dependency

@Service
public class OrderService {

    public OrderService(
            PaymentService paymentService) {
    }
}

@Service
public class PaymentService {

    public PaymentService(
            OrderService orderService) {
    }
}

This design creates a dependency cycle.

Rather than immediately introducing lazy injection to hide the problem, examine whether the responsibilities should be separated. A third orchestration service, domain event, or narrower interface may remove the cycle.

How to troubleshoot startup failures

  • Read the complete dependency-creation exception chain.
  • Identify circular dependencies and unexpected bean conditions.
  • Review initialization methods and constructors for external calls.
  • Check profile-specific configuration and conditional beans.
  • Move expensive work into an explicit lifecycle or startup process when appropriate.

Senior-level takeaway: Bean initialization should be predictable and should avoid unnecessary external side effects. Design dependencies so the application can start and recover cleanly.

7. Why can @Async fail to execute asynchronously when one method calls another method in the same class?

Spring commonly implements @Async through a proxy. When a call passes through the proxy, Spring can intercept the invocation and execute it using the configured task executor.

Consider this example:

@Service
public class ReportService {

    public void generateReports() {
        generateAsync();
    }

    @Async
    public void generateAsync() {
        // Expensive operation
    }
}

When generateReports() invokes generateAsync() directly on the same object, the call is known as self-invocation. It bypasses the Spring proxy, so the @Async interceptor is not applied as expected.

Recommended solution: Separate the asynchronous operation into another bean

@Service
public class AsyncReportService {

    @Async("reportExecutor")
    public CompletableFuture<Void> generateAsync() {
        // Perform report generation

        return CompletableFuture.completedFuture(null);
    }
}
@Service
public class ReportService {

    private final AsyncReportService asyncReportService;

    public ReportService(
            AsyncReportService asyncReportService) {
        this.asyncReportService = asyncReportService;
    }

    public void generateReports() {
        asyncReportService.generateAsync();
    }
}

Enable asynchronous execution with @EnableAsync and configure an appropriate executor.

Other important considerations

  • Verify that the asynchronous method is eligible for interception under the chosen proxy mechanism.
  • Configure thread-pool size and queue capacity based on workload and resource limits.
  • Handle exceptions from asynchronous operations.
  • Track task rejection, execution time, and queue depth.
  • Do not assume that asynchronous execution automatically makes a workflow durable.

Senior-level takeaway: An annotation does not guarantee that an operation runs asynchronously. Understand proxy boundaries, executor configuration, and how failures are reported.

Section 3: Distributed Transactions and Spring Batch

8. How would you handle distributed transactions when a business operation spans multiple microservices?

Consider an e-commerce workflow:

Order Service
     |
     v
Payment Service
     |
     v
Inventory Service
     |
     v
Shipping Service

Each service may own its own database. A local Spring transaction generally controls resources within its configured transaction manager; it does not automatically make updates across independently managed service databases atomic.

If payment succeeds but inventory reservation fails, the business workflow may be left in a partially completed state.

Approach 1: Saga pattern

A Saga breaks a distributed business operation into a sequence of local transactions. If a later step fails, the workflow may execute compensating actions to reverse or offset earlier business effects.

Example:

1. Create order
2. Reserve inventory
3. Authorize payment
4. Confirm order

If payment fails:
5. Release inventory
6. Mark order as failed

Compensation is not necessarily a literal database rollback. A captured payment may require a refund, which is a separate business operation and may itself fail.

Approach 2: Orchestration

A central workflow component coordinates the steps, tracks progress, handles timeouts, and decides which action comes next.

This makes workflow visibility clearer, but the orchestrator becomes an important component that must itself be made reliable.

Approach 3: Choreography

Services react to events published by other services. For example, an order-created event triggers inventory reservation, which publishes an inventory-reserved event that allows the workflow to continue.

Choreography reduces central coordination but can become difficult to understand when many services and failure paths are involved.

Reliability requirements

  • Make operations idempotent.
  • Persist workflow state.
  • Use reliable event publishing and consumption patterns.
  • Define timeout, retry, compensation, and terminal-failure behavior.
  • Provide reconciliation for workflows stuck in intermediate states.
  • Expose workflow status for operations and customer support.

Senior-level takeaway: Distributed business workflows usually require explicit consistency and recovery design. Do not assume that a local @Transactional annotation creates an atomic transaction across microservices.

9. How would you design a Spring Batch job that processes millions of records while supporting restartability and fault tolerance?

Processing millions of records in a single in-memory collection can consume excessive memory, increase garbage-collection pressure, and make recovery difficult.

Spring Batch commonly supports chunk-oriented processing, where items are read, processed, and written in manageable groups.

Reader
   |
   v
Processor
   |
   v
Writer
   |
   v
Commit chunk
   |
   v
Continue with next chunk

For example, a job might read customer records, calculate updated eligibility, and write the results in chunks of a configured size.

Key design principles

1. Use bounded chunk sizes.

Choose a chunk size based on memory usage, database behavior, transaction cost, and failure-recovery requirements. A larger chunk may reduce commit overhead but increases the amount of work affected by a failure.

2. Persist job execution metadata.

Spring Batch job repositories record job and step execution state, allowing supported restart scenarios to resume from saved execution information.

3. Make processing restartable.

Use readers and writers whose state and checkpoint behavior are appropriate for the source and destination. Ensure restart logic does not incorrectly skip or repeat business work.

4. Make writes idempotent where possible.

If a failure occurs around a commit boundary or an external side effect, retries can potentially repeat work. Design writes and external operations to tolerate duplicates.

5. Handle invalid records explicitly.

Use skip policies for appropriate data-quality failures and retry policies for transient failures. Record rejected data so it can be reviewed and corrected.

6. Monitor progress.

Track read, process, and write counts; duration; failure rates; skip counts; throughput; and estimated completion time.

Important production considerations

  • Use database pagination or cursor-based reading appropriately.
  • Review transaction boundaries and database locking.
  • Control concurrency to avoid overwhelming the database.
  • Define how job parameters determine whether a new execution or a restart is intended.
  • Test restart behavior by deliberately interrupting jobs.
  • Separate the batch metadata repository from business tables conceptually, even if they share infrastructure.

Senior-level takeaway: A production batch job must be designed for restart, observability, data quality, and controlled resource usage—not just maximum throughput.

10. What is the difference between skip, retry, and recovery in Spring Batch?

Skip, retry, and recovery address different failure scenarios.

Mechanism Purpose Typical Example
Skip Continue processing after an eligible item failure Reject a malformed input record
Retry Attempt an operation again when a failure may be transient Temporary database or remote-service failure
Recovery Apply a defined action after retry or failure handling cannot complete normally Record a rejected item or move it to a recovery workflow

Skip example: A CSV file contains a record with an invalid date. If the business policy allows the job to continue, the record can be skipped within a configured limit and recorded for correction.

Retry example: A database write fails because of a transient connection problem. Retrying may succeed after the connection recovers.

Recovery example: If a record continues to fail after permitted retries, the application may store it in a rejection table, send it to a dead-letter workflow, or raise an operational alert.

Not every exception should be skipped or retried. A permanent schema error, invalid configuration, or deterministic business-rule violation may never improve with another attempt.

Similarly, a retry can be dangerous when an operation has side effects and its completion status is unknown. For example, a remote payment request may have succeeded even though the response timed out.

Senior-level takeaway: Classify exceptions before defining skip and retry policies. Use retry for potentially transient failures, skip for acceptable item-level failures, and recovery for explicitly designed handling of work that cannot complete normally.

11. How would you prevent two instances of the same scheduled Spring Batch job from processing the same data simultaneously?

When an application is deployed on multiple instances, each instance may run its own scheduled method. Without coordination, two instances can start the same logical job at the same time.

For example:

Instance A ----+
               |
               +---- Process daily settlement
               |
Instance B ----+

This can result in duplicate payments, repeated notifications, or concurrent updates to the same records.

Approach 1: Use Spring Batch job identity correctly

Spring Batch uses job names and identifying job parameters to distinguish job instances. Its job repository coordinates execution metadata, but correct job identity alone does not guarantee that arbitrary business data cannot be processed concurrently by different jobs or different parameter sets.

Approach 2: Use distributed scheduling coordination

A distributed lock or scheduler designed for clustered execution can ensure that only one instance runs a particular scheduled task at a time. The lock must have appropriate expiry, ownership, and failure-recovery behavior.

Approach 3: Claim work atomically

For record-level processing, mark records as claimed or move them through explicit processing states using atomic database operations. This helps prevent different workers from claiming the same work.

Approach 4: Make business operations idempotent

Even with distributed locks, retries, crashes, and uncertain outcomes can create duplicate attempts. Use unique business keys, database constraints, idempotency records, and safe update semantics.

Production checks:

  • Check whether all application instances run the same scheduler.
  • Inspect job parameters and job repository execution history.
  • Review distributed lock acquisition and expiry behavior.
  • Verify how claimed records are recovered after a worker crashes.
  • Monitor duplicate attempts and concurrent executions.

Senior-level takeaway: Scheduling coordination, job identity, atomic work claiming, and idempotency solve different parts of the duplicate-processing problem. A robust design considers all of them.

12. How would you process a large CSV file containing invalid records without failing the entire batch job?

In real production files, some records may be malformed or violate business rules. Failing the entire job for one invalid record can prevent thousands or millions of otherwise valid records from being processed.

A robust design separates file parsing, validation, business processing, and error reporting.

1. Validate records explicitly

Check required fields, date formats, numeric ranges, identifier formats, and business constraints before writing data.

2. Classify errors

  • Recoverable record error: The record is invalid, but the job can safely continue.
  • Transient infrastructure error: A database or remote dependency is temporarily unavailable.
  • Fatal job error: The file format is fundamentally wrong, a required configuration is missing, or the destination is unavailable in a way that prevents safe processing.

3. Configure skip and retry policies

Use a skip policy for known, acceptable record-level failures, with a maximum skip count. Use retry for selected transient exceptions, and avoid retrying deterministic validation failures.

4. Capture rejected records

For each rejected record, retain enough information to identify and correct it, such as the source file, row number, validation error, and a safe representation of the input.

Be careful not to write sensitive personal information into unrestricted error logs.

5. Produce a job summary

File: customers.csv

Records read:       1,000,000
Records processed:    992,450
Records rejected:       7,500
Infrastructure errors:      50

The exact treatment of infrastructure errors depends on whether they are retried, cause a restart, or terminate the job. The report should reflect the actual job outcome rather than treating every non-processed record as an ordinary validation rejection.

6. Support replay and correction

Rejected records should be recoverable through a defined correction and reprocessing workflow. Ensure that replaying them cannot accidentally duplicate previously successful business operations.

Senior-level takeaway: Fault-tolerant batch processing requires clear error classification, bounded skip and retry policies, auditable rejection handling, and safe reprocessing.

Section 4: Spring AI, RAG, and AI Application Security

13. How does Retrieval-Augmented Generation (RAG) reduce hallucinations in Spring AI applications?

Retrieval-Augmented Generation, or RAG, combines information retrieval with a large language model (LLM). Instead of relying only on the model's internal knowledge, the application retrieves relevant information from a trusted knowledge source and supplies it as context for generating a response.

A typical RAG architecture looks like this:

User Question
      |
      v
Embedding / Query Processing
      |
      v
Vector or Hybrid Search
      |
      v
Retrieve Relevant Documents
      |
      v
Build Grounded Prompt
      |
      v
LLM Generates Answer
      |
      v
Return Answer with Sources

For example, an internal HR assistant might retrieve the company's leave policy before answering a question about annual leave. The model can then base its response on the retrieved policy instead of guessing from general training data.

Why RAG helps

  • Provides relevant source material at query time.
  • Allows information to be updated without retraining the model.
  • Can improve answers about private or domain-specific information.
  • Can provide source references for verification.
  • Can reduce unsupported answers when retrieval and generation are designed well.

Why RAG does not eliminate hallucinations

The retriever may return irrelevant or incomplete documents. The model may misunderstand the context, combine conflicting facts, or generate claims that are not supported by the retrieved material.

Therefore, production RAG systems should also include:

  • Retrieval-quality evaluation.
  • Source-aware prompts and instructions to abstain when evidence is insufficient.
  • Access controls on retrieved documents.
  • Grounding and citation checks.
  • Evaluation datasets representing real user questions.
  • Monitoring for incorrect or unsupported answers.

Senior-level takeaway: RAG improves grounding by supplying relevant evidence, but answer quality depends on retrieval, context construction, model behavior, and verification.

14. How would you handle document chunking, embedding generation, and vector storage when building a RAG pipeline?

A RAG pipeline commonly has two major workflows: indexing documents and answering user questions.

Indexing workflow

Documents
    |
    v
Extract Text and Metadata
    |
    v
Clean and Normalize
    |
    v
Split into Chunks
    |
    v
Generate Embeddings
    |
    v
Store Vectors and Metadata

1. Extract and clean documents

Documents may come from PDFs, HTML pages, Word files, internal knowledge bases, or databases. Preserve important structure such as headings, section names, document IDs, timestamps, and access-control metadata.

2. Choose an appropriate chunking strategy

Very large chunks can contain irrelevant information, while very small chunks can lose context. Use document-aware chunking where possible, preserving paragraphs, sections, tables, and meaningful boundaries.

Chunk size and overlap should be selected through evaluation rather than a universal rule. The right settings depend on the document structure, embedding model, context window, and query types.

3. Generate embeddings

An embedding model converts text into numerical vectors that represent semantic characteristics. Documents and user queries must use compatible embedding representations for meaningful similarity search.

4. Store vectors and metadata

Store the embedding alongside relevant metadata such as document ID, chunk ID, source, version, tenant, and access-control attributes.

5. Retrieve relevant chunks

At query time, generate an embedding for the user's question, retrieve candidate chunks, and optionally apply keyword search, metadata filtering, or reranking.

6. Construct the model context

Supply the selected evidence to the LLM with clear instructions about how to use it. Respect context-window limits and avoid including irrelevant documents.

7. Maintain the index

  • Detect changed or deleted documents.
  • Re-index affected chunks.
  • Track embedding model and index versions.
  • Remove obsolete vectors when documents are deleted.
  • Apply tenant and user permissions during retrieval.
  • Monitor indexing failures and embedding costs.

Important security consideration: Filtering results after retrieval may be too late if unauthorized content has already entered the model context. Enforce access control as part of retrieval and context construction.

Senior-level takeaway: A production RAG system is a data pipeline with retrieval, indexing, access control, versioning, and evaluation—not merely a vector database connected to an LLM.

15. How would you prevent prompt injection attacks in an AI-powered Spring Boot application?

Prompt injection occurs when untrusted input attempts to influence an LLM to ignore intended instructions, disclose information, or perform unauthorized actions.

It can originate from user prompts, uploaded documents, retrieved web pages, emails, or other external content included in the model context.

For example, a retrieved document might contain text instructing the model to ignore its original task and reveal confidential information.

1. Treat external content as untrusted data

Do not treat instructions found in retrieved documents as authoritative application policy. Clearly separate trusted instructions from user input and retrieved content in the prompt design.

2. Enforce authorization outside the model

The LLM must not be the only mechanism deciding whether a user can read a document, access an account, or execute a transaction. Enforce permissions in the application and data-access layers.

3. Restrict tool permissions

If the model can call tools, give each tool the minimum permissions required. Use explicit allowlists, validate arguments, and require authorization before performing sensitive actions.

4. Validate tool inputs and outputs

Do not blindly execute SQL, shell commands, URLs, or arbitrary API calls generated by the model. Use constrained interfaces and validated parameters.

5. Protect retrieved information

Apply access controls before information enters the model context. Avoid mixing confidential data from different users or tenants in a shared prompt.

6. Add confirmation for high-impact actions

Require explicit approval or a separate authorization step for payments, account changes, destructive actions, and other high-risk operations.

7. Test adversarial scenarios

Test prompts that attempt to override system instructions, reveal secrets, misuse tools, or exploit retrieved documents. Evaluate both direct and indirect prompt-injection cases.

Senior-level takeaway: Prompt engineering alone cannot guarantee security. Treat the model as an untrusted decision-making component and enforce access control, validation, and side-effect authorization in deterministic application code.

16. How would you evaluate the quality of AI-generated responses before releasing a Spring AI application to production?

AI applications need evaluation beyond checking whether the endpoint returns HTTP 200. A response can be syntactically valid and still be inaccurate, unsupported, unsafe, or irrelevant.

1. Create a representative evaluation dataset

Build a set of realistic questions with expected answers, acceptable variations, relevant source documents, and examples of questions the system should refuse or escalate.

2. Measure retrieval quality

For RAG systems, evaluate whether the correct documents and chunks appear in the retrieved results. Useful measures can include precision, recall, ranking quality, and whether the required evidence is present in the final context.

3. Evaluate answer quality

Assess dimensions such as:

  • Factual correctness.
  • Relevance to the question.
  • Grounding in the retrieved evidence.
  • Completeness.
  • Correct citation or source attribution.
  • Appropriate abstention when evidence is insufficient.

4. Evaluate safety and security

Test prompt injection, unauthorized information retrieval, sensitive-data leakage, unsafe tool calls, and incorrect authorization behavior.

5. Evaluate latency and cost

Measure response latency, token consumption, model errors, timeout rates, concurrency behavior, and the cost of processing representative workloads.

6. Use regression tests

Run the evaluation dataset when changing the model, prompt, embedding model, chunking strategy, retriever, or tool configuration.

7. Deploy gradually

Start with a limited rollout, compare quality and operational metrics, and retain a rollback path if the new version performs worse.

Automated LLM-as-a-judge evaluation can help scale assessments, but it should be calibrated against human-reviewed examples. It is not a substitute for independent verification of critical outcomes.

Senior-level takeaway: Evaluate AI quality, security, latency, and cost as separate dimensions. A model upgrade is not automatically an application-quality improvement.

17. How would you control token usage and cost when thousands of users invoke an LLM simultaneously?

When an AI application scales, model cost and concurrency can become major production constraints. A request may consume tokens for system instructions, conversation history, retrieved documents, tool results, and the generated response.

A simplified cost model is:

Total Cost =
    Input Tokens × Input Token Price
  + Output Tokens × Output Token Price

Actual billing depends on the model and provider, including any cached-token, tool, or other pricing rules.

1. Limit input and output tokens

Set sensible maximums for request size, retrieved context, conversation history, and generated output. Do not send an entire document collection when only a few relevant passages are needed.

2. Retrieve more selectively

Improve search relevance, use metadata filters, rerank candidates, and remove redundant chunks before building the prompt.

3. Select models by task

Use a lower-cost model for straightforward tasks where evaluation shows acceptable quality, and reserve more capable models for tasks that need them.

4. Apply concurrency controls

  • Use bounded request concurrency.
  • Apply per-user or per-tenant rate limits.
  • Use bounded queues with explicit overload behavior.
  • Respect provider rate limits.
  • Set request deadlines and timeouts.
  • Reject or defer excess work rather than allowing unbounded resource growth.

5. Cache carefully

Cache reusable embeddings, stable retrieval results, or eligible responses when correctness and data-isolation requirements permit it. Include relevant user, tenant, permission, and version context in cache decisions.

6. Monitor cost and quality

Track input and output tokens, cost per request, cost per tenant, model latency, error rates, retries, and output quality.

7. Avoid retry storms

When a provider is overloaded, unrestricted retries can multiply traffic and cost. Use bounded retries, backoff, jitter, and circuit-breaking strategies where appropriate.

Senior-level takeaway: AI cost control is a combination of prompt design, retrieval quality, model selection, admission control, caching, and operational monitoring.

Section 5: Event-Driven Microservices and Kafka

18. How would you design an event-driven microservice that guarantees reliable event publishing without losing database updates?

Consider an order service that saves an order in a database and then publishes an event to Kafka.

A naive implementation might do this:

@Transactional
public void createOrder(Order order) {
    orderRepository.save(order);

    kafkaTemplate.send(
        "orders",
        new OrderCreatedEvent(order.getId())
    );
}

This code does not automatically make the database transaction and Kafka publication one atomic operation.

For example:

  • The database transaction commits, but Kafka publication fails.
  • The event is published, but the database transaction later rolls back.
  • The process crashes between the two operations.

Recommended solution: Transactional outbox pattern

Write both the business update and an outbox record within the same database transaction.

@Transactional
public void createOrder(Order order) {

    Order savedOrder =
        orderRepository.save(order);

    OutboxEvent event = new OutboxEvent(
        "Order",
        savedOrder.getId().toString(),
        "ORDER_CREATED",
        serialize(savedOrder)
    );

    outboxRepository.save(event);
}

The order and outbox record are committed or rolled back together within the same database transaction.

A separate publisher reads pending outbox records and publishes them to Kafka. After successful publication, it records the appropriate delivery state.

Business Transaction
        |
        +-- Save Order
        |
        +-- Save Outbox Event
        |
        v
Database Commit
        |
        v
Outbox Publisher
        |
        v
Kafka Topic
        |
        v
Consumer

What happens if publishing succeeds but marking the outbox record fails?

The publisher may publish the same event again when it retries. Therefore, the outbox pattern helps prevent lost events but does not by itself guarantee exactly-once business effects across the database and Kafka.

Consumers should be idempotent, typically using a unique event ID and a durable mechanism to prevent duplicate processing.

Additional considerations

  • Use bounded retries and monitor old pending outbox records.
  • Define ordering requirements and partitioning strategy.
  • Handle poison events and serialization failures.
  • Ensure consumers can recover after crashes.
  • Use change data capture where it fits the architecture.

Senior-level takeaway: The outbox pattern coordinates reliable event publication with database changes. Idempotent consumers and operational recovery complete the reliability design.

19. How would you troubleshoot a Kafka consumer that repeatedly processes the same messages after deployment?

Repeated processing can occur when a consumer processes a message but its progress is not committed, when it loses its group assignment, or when application behavior causes a message to be retried.

Common causes include:

  • Consumer crashes after processing but before offset commit.
  • Rebalance during long-running processing.
  • Processing time exceeding configured polling constraints.
  • Exceptions causing repeated retries.
  • Offset commit failures.
  • Deployment-related changes to consumer group IDs.
  • Incorrect error-handler or acknowledgment configuration.
  • Application-side duplicate processing despite successful offset commits.

Step 1: Check consumer group identity

Verify that the new deployment uses the intended consumer group ID. If the group ID changed, the new group may begin reading according to its offset-reset configuration instead of continuing the previous group's progress.

Step 2: Inspect offset behavior

Compare committed offsets, current consumer positions, lag, and the records being processed. Confirm whether the consumer commits automatically or manually and whether the acknowledgment behavior matches the application design.

Step 3: Inspect processing duration

If processing takes too long relative to the consumer's polling configuration, the consumer may leave the group and trigger a rebalance. Another consumer can then receive the same uncommitted records.

Step 4: Check error handling

Look for repeated exceptions, retry loops, deserialization failures, and dead-letter configuration problems. Confirm whether the same record is failing repeatedly or whether multiple records are being replayed.

Step 5: Make business processing idempotent

Kafka consumers can encounter duplicate delivery under common at-least-once processing designs. Use event identifiers, unique constraints, deduplication records, or safe state transitions to prevent repeated business effects.

For example, a payment event should not trigger a second charge merely because the consumer processed the same message again.

Step 6: Compare deployment changes

Review changes to group IDs, concurrency, acknowledgment mode, error handlers, polling settings, serialization, and processing timeouts.

Senior-level takeaway: Repeated Kafka delivery is not always a broker defect. Investigate offsets, consumer lifecycle, error handling, deployment configuration, and the idempotency of the business operation.

Section 6: End-to-End Performance and Distributed Tracing

20. Your Spring Boot application responds quickly in isolation, but the complete user journey is slow. How would you trace latency across the API gateway, microservices, database, Kafka, and AI model?

A single endpoint can look fast in an isolated test while the full user journey remains slow because the request crosses multiple services and asynchronous components.

For example:

Web / Mobile Client
        |
        v
API Gateway
        |
        v
Order Service
        |
        +---- Database
        |
        +---- Kafka
        |
        +---- AI Service
                  |
                  v
              LLM Provider

Each component contributes latency, and some work may happen asynchronously after the initial HTTP response.

1. Measure the complete journey

Establish end-to-end latency and identify whether the issue affects all requests or only particular operations, tenants, or traffic patterns. Use percentiles such as p50, p95, and p99 rather than relying only on average response time.

2. Use distributed tracing

Instrument the gateway and services so a trace shows how long each span takes and how requests move between components.

A trace might reveal:

Total request latency: 2,400 ms

API Gateway:             40 ms
Order Service:          180 ms
Database query:         120 ms
AI model call:        1,700 ms
Other processing:       360 ms

These figures are illustrative. The point is to identify which operation dominates the latency instead of assuming that the Java controller is responsible.

3. Correlate logs and metrics

Use trace IDs and request IDs to correlate structured logs across services. Metrics help identify whether latency is associated with saturation, traffic changes, error rates, or a particular dependency.

4. Investigate the database

Inspect query duration, query plans, indexes, lock waits, connection-pool acquisition time, and the number of database round trips. A fast query measured manually may behave differently under production concurrency.

5. Investigate Kafka

For asynchronous workflows, monitor consumer lag, processing duration, retries, partition distribution, and time spent waiting for downstream operations.

Kafka processing may not contribute to the initial HTTP response if it is asynchronous. In that case, measure business-workflow completion time separately from request latency.

6. Investigate AI model latency

Separate retrieval time, prompt construction, model-provider latency, time to first token, total generation time, token consumption, and retry overhead. Large prompts and unnecessary context can increase both latency and cost.

7. Check infrastructure saturation

  • CPU throttling and CPU saturation.
  • Heap usage and garbage-collection pauses.
  • Thread-pool exhaustion and blocked threads.
  • Database connection-pool saturation.
  • Network latency and connection reuse.
  • Downstream rate limits and timeouts.

8. Optimize the measured bottleneck

If model inference dominates, investigate model selection, prompt length, retrieval quality, streaming, and caching. If database time dominates, inspect SQL and connection contention. If a thread pool is saturated, determine whether work is blocking, excessive, or poorly bounded.

Do not optimize every component at once. Make a targeted change, measure the result, and check that the improvement does not create a new bottleneck elsewhere.

Senior-level takeaway: End-to-end performance is a distributed-system property. Distributed traces, metrics, structured logs, and workload-aware tests help identify where time is actually spent.

Production Checklist for Senior Java Developers

Before releasing a Spring Boot application, review these areas:

  • Security: Method authorization, ownership checks, CSRF assumptions, JWT lifecycle, and tenant isolation.
  • Bean lifecycle: Dependency cycles, initialization failures, and external calls during startup.
  • Asynchronous execution: Proxy boundaries, bounded executors, exception handling, and task monitoring.
  • Distributed workflows: Saga design, idempotency, compensating actions, and reconciliation.
  • Batch processing: Restartability, skip/retry policy, checkpoints, and duplicate prevention.
  • AI applications: Retrieval quality, prompt-injection defenses, evaluation, token limits, and cost controls.
  • Event-driven systems: Outbox publishing, consumer idempotency, offset management, and poison-message handling.
  • Observability: Distributed traces, p95/p99 latency, structured logs, dependency metrics, and actionable alerts.

Common Mistakes to Avoid

  • Assuming JWT logout immediately invalidates every previously issued access token.
  • Checking authentication without enforcing resource ownership.
  • Disabling CSRF without understanding how browser credentials are sent.
  • Assuming a local database transaction makes multiple microservices atomic.
  • Using unlimited retries for permanent failures.
  • Assuming the outbox pattern eliminates duplicate delivery.
  • Running large batch jobs without restart and recovery testing.
  • Assuming RAG completely eliminates hallucinations.
  • Allowing an LLM to authorize sensitive operations without deterministic checks.
  • Assuming a fast individual service guarantees a fast end-to-end workflow.

Frequently Asked Questions

1. What are the most important Spring Security topics for senior interviews?

Focus on method-level authorization, JWT lifecycle and revocation, resource ownership, CSRF, and tenant isolation. Be prepared to explain both the security mechanism and its limitations.

2. How do you make a Spring Batch job restartable?

Use appropriate job and step configuration, persisted execution metadata, suitable readers and writers, clear job parameters, and idempotent business operations. Test restart behavior after failures rather than assuming it will work automatically.

3. Does Spring AI RAG eliminate hallucinations?

No. RAG supplies relevant evidence to the model, but retrieval can be incorrect or incomplete and the model can still produce unsupported answers. Evaluate retrieval and generation quality separately.

4. How can you prevent losing events after a database update?

The transactional outbox pattern stores the business update and event record in the same database transaction. A separate publisher delivers the event, while idempotent consumers handle possible duplicate delivery.

5. How do you troubleshoot a slow microservices workflow?

Use distributed tracing to identify expensive spans, correlate them with structured logs and metrics, inspect database and downstream behavior, and distinguish synchronous request latency from asynchronous workflow completion time.

Conclusion

Senior developers are not defined by how many annotations they know. They are defined by how well they handle security, reliability, scalability, and complex production problems.

A strong developer understands why a JWT remains valid after logout, how a batch job recovers after a failure, why database transactions and Kafka publishing can become inconsistent, and why RAG does not guarantee correct AI responses.

More importantly, a senior engineer can explain the trade-offs, identify failure scenarios, design recovery mechanisms, and prove that a solution works under realistic conditions.

Which question would you find the hardest?

Prepare for Your Next Java Interview

Java Spring Boot & Microservices Interview Q&A (200 Questions)
https://lnkd.in/gqhJhmcZ

Full Interview Kit (600 Questions)
Java, Spring Boot, database, DevOps, cloud, system design, and microservices.
https://lnkd.in/gPi2W587

Complete Java Full Stack Developer Kit (900 Questions)
Core Java, Spring Boot and microservices, cloud, DevOps, system design, React, and Angular.
https://topmate.io/logicbrace/2306382

Senior Java and Spring Boot Interview Questions Top 20 Part 7 covering Spring Security, Spring Batch, Spring AI, Kafka, and distributed systems


Post a Comment

0 Comments

Close Menu