Prev Next

Java / J2EE Design Patterns with Java 21 Interview questions

Last updated

1. What is a design pattern in J2EE? 2. What are the categories of J2EE design patterns? 3. What is the MVC pattern? 4. What is the Front Controller pattern? 5. What is the Singleton pattern? 6. What is the Factory Method pattern? 7. What is the DAO pattern? 8. What is a Data Transfer Object? 9. What is the Business Delegate pattern? 10. What is the Service Locator pattern? 11. What is the Session Facade pattern? 12. What is the Intercepting Filter pattern? 13. What is the View Helper pattern? 14. What is the Composite View pattern? 15. What is the Value List Handler pattern? 16. What is the Observer pattern? 17. What is the Builder pattern? 18. What is the Strategy pattern? 19. Describe the Proxy pattern? 20. What is the Template Method pattern? 21. How do records simplify DTOs in Java 21? 22. How do sealed interfaces improve Strategy and Command? 23. How does pattern matching for switch replace the Visitor pattern? 24. Why is Singleton risky in clustered J2EE applications? 25. How does dependency injection replace Service Locator? 26. What is the difference between DAO and Repository? 27. What is the difference between Factory Method and Abstract Factory? 28. How do virtual threads affect thread-per-request designs? 29. How does Intercepting Filter work with Jakarta Servlet filters? 30. How does Spring AOP use the Proxy pattern? 31. How do you implement a thread-safe Singleton? 32. What is the difference between Decorator and Proxy? 33. When should you use Session Facade? 34. Explain the request lifecycle in Front Controller and MVC? 35. What is the difference between Strategy and State? 36. How does the Command pattern support undo and async work? 37. How do you implement Observer with CDI events? 38. How do Sequenced Collections help build an LRU cache? 39. How does the Circuit Breaker pattern work? 40. How does the Saga pattern manage distributed transactions? 41. Why is Business Delegate rarely used today? 42. How does Spring JdbcTemplate use Template Method? 43. What is the difference between Adapter and Facade? 44. How do you implement Chain of Responsibility in Java 21? 45. How does optimistic locking work with JPA @Version? 46. What is the CQRS pattern and when should you use it? 47. What is the difference between Event Sourcing and CRUD? 48. How do Scoped Values replace ThreadLocal for request context? 49. Which classic J2EE patterns are obsolete today? 50. How do you avoid over-engineering with design patterns?

1. What is a design pattern in J2EE?

A design pattern in J2EE is a proven, named solution to a problem that keeps showing up when you build multi-tier enterprise applications. It is not a library or a class you import. It is a template for how to arrange classes and responsibilities across the web, business and data tiers.

The best-known catalog is Core J2EE Patterns by Alur, Crupi and Malks. It describes patterns such as Front Controller, Session Facade and Data Access Object. The platform has since been renamed Jakarta EE, but the underlying problems (request handling, remote calls, persistence) are the same.

Patterns give teams a shared vocabulary. Saying "put a Session Facade in front of those services" is faster than describing the whole structure.

Take quiz
A J2EE design pattern is best described as:
a Maven plugin that generates boilerplate
a reusable, named solution to a recurring design problem
a vendor-specific application server feature
a JDBC driver optimisation
Where does the classic J2EE pattern catalog come from?
the JDK 21 release notes
the Servlet 6.0 specification only
the JPA reference implementation
Core J2EE Patterns by Alur, Crupi and Malks

2. What are the categories of J2EE design patterns?

Core J2EE patterns are grouped by the tier they belong to: presentation, business and integration.

Presentation-tier patterns deal with web requests and views. Business-tier patterns organise services and domain logic. Integration-tier patterns hide how the application talks to databases, messaging systems and external services.

Tier Typical patterns
Presentation Intercepting Filter, Front Controller, View Helper, Composite View
Business Business Delegate, Session Facade, Service Locator, Transfer Object
Integration Data Access Object, Service Activator, Web Service Broker

Many teams also use the general GoF patterns (Singleton, Factory, Strategy) inside each tier.

Take quiz
Which tier does the Data Access Object pattern belong to?
presentation tier
client tier
integration tier
none, it is a GoF-only pattern
Intercepting Filter is classified under which tier?
presentation tier
business tier
integration tier
resource tier

3. What is the MVC pattern?

MVC splits a web application into three roles: Model (data and business state), View (what the user sees) and Controller (handles input and decides what happens next).

In a Jakarta EE app, a servlet or a framework controller receives the request, calls a service to update the model, and then forwards to a view such as a JSP, Facelets page or JSON serializer.

The main benefit is that the view can change without touching business rules, and business rules can be tested without a browser.

Take quiz
In MVC, which component receives the user's request and chooses the next step?
Model
View
Data source
Controller
What is the main benefit of MVC?
it removes the need for a database
separation of UI, input handling and business state
it makes every request run in a single thread
it replaces dependency injection

4. What is the Front Controller pattern?

Front Controller routes every incoming request through one central handler instead of letting each page process its own request. That handler does common work (authentication, logging, locale) and then dispatches to the right command or controller.

Spring MVC's DispatcherServlet, Jakarta Faces' FacesServlet and Struts' ActionServlet are all front controllers.

@WebServlet("/app/*")
public class FrontServlet extends HttpServlet {
    private final Map<String, Command> routes = Map.of(
        &quot;/orders&quot;, new OrderCommand(),
        &quot;/users&quot;,  new UserCommand());

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws IOException {
        Command c = routes.get(req.getPathInfo());
        if (c == null) { res.sendError(404); return; }
        c.execute(req, res);
    }
}

Take quiz
What is the main job of a Front Controller?
handle all requests centrally and dispatch them
cache database rows
generate SQL
manage JVM garbage collection
Which class is a well-known Front Controller?
java.util.Timer
javax.sql.DataSource
Spring's DispatcherServlet
java.lang.ThreadLocal

5. What is the Singleton pattern?

Singleton ensures a class has exactly one instance per JVM (strictly, per class loader) and gives a global access point to it. Typical uses are configuration holders, registries and caches.

In Java 21 the safest plain-Java version is a single-element enum, because the JVM guarantees one instance even with serialization and reflection attacks.

public enum AppConfig {
    INSTANCE;
    private final Properties props = load();
    public String get(String key) { return props.getProperty(key); }
    private static Properties load() { /* read file */ return new Properties(); }
}

Take quiz
Which approach is serialization-safe and reflection-safe for Singleton?
a public constructor with a static counter
a single-element enum
a non-final static field set by any caller
cloning a prototype object
A Singleton guarantees one instance per:
server cluster
database
HTTP session
class loader in a JVM

6. What is the Factory Method pattern?

Factory Method defines an interface for creating an object but lets subclasses or a dedicated method decide which concrete class to instantiate. The caller depends on the abstraction, not on new ConcreteClass().

A common J2EE example is a DaoFactory that returns a MySQL or Oracle implementation of the same DAO interface, based on configuration.

interface Notifier { void send(String msg); }
class EmailNotifier implements Notifier { public void send(String m) { /* smtp */ } }
class SmsNotifier  implements Notifier { public void send(String m) { /* sms  */ } }

class NotifierFactory {
    static Notifier of(String type) {
        return switch (type) {
            case &quot;email&quot; -> new EmailNotifier();
            case &quot;sms&quot;   -> new SmsNotifier();
            default -> throw new IllegalArgumentException(type);
        };
    }
}

Take quiz
What does Factory Method hide from the caller?
the HTTP status code
the thread priority
which concrete class gets instantiated
the database schema
Why is a factory useful for DAOs?
you can swap implementations without changing callers
it makes SQL run faster
it removes the need for interfaces
it enables multiple inheritance

7. What is the DAO pattern?

The Data Access Object pattern puts all database access for one entity behind an interface. Business code calls userDao.findById(7) and never sees JDBC, SQL or JPA details.

This keeps persistence swappable (JDBC today, JPA tomorrow) and makes services easy to unit test by mocking the DAO.

public interface UserDao {
    Optional<User> findById(long id);
    List<User> findByCity(String city);
    void save(User user);
    void delete(long id);
}

Take quiz
What does a DAO encapsulate?
HTTP routing
UI rendering
thread scheduling
persistence logic for an entity
What is a testing benefit of DAO?
tests no longer need assertions
services can be tested with a mocked DAO
it removes the need for a build tool
it guarantees faster SQL

8. What is a Data Transfer Object?

A Data Transfer Object (called Transfer Object in the Core J2EE catalog) is a simple carrier of data between layers or across a network call. It holds fields and nothing else, so one call can move several values at once instead of many chatty getter calls.

In Java 21 a record is the natural way to write one: immutable, concise and with equals, hashCode and toString generated.

public record UserDto(long id, String name, String email) {}

Take quiz
What should a DTO contain?
data fields only, no business logic
database connections
servlet filters
transaction managers
Which Java 21 construct is ideal for DTOs?
enum
abstract class
record
synchronized block

9. What is the Business Delegate pattern?

Business Delegate is a client-side object that stands between the presentation tier and the business services. The web layer calls the delegate, and the delegate handles lookup, retries and translating remote exceptions into application exceptions.

The point was to shield UI code from the details of EJB remote calls. If the service API changes, only the delegate changes.

Take quiz
Which tier does a Business Delegate serve?
it sits between the database and the disk
it sits between presentation and business tiers
it replaces the servlet container
it lives only inside the JVM garbage collector
Business Delegate mainly reduces:
JVM start-up time
network latency to zero
the number of database tables
coupling between the UI and remote service details

10. What is the Service Locator pattern?

Service Locator is a central class that looks up services (JNDI resources, EJBs, data sources) by name and caches them. Clients ask the locator instead of doing their own JNDI lookups, which are costly and repetitive.

Today dependency injection (CDI @Inject, Spring) does the same job without a global lookup class, so Service Locator is mostly seen in legacy code.

public final class ServiceLocator {
    private static final Map<String, Object> cache = new ConcurrentHashMap<>();
    public static <T> T lookup(String jndiName, Class<T> type) {
        return type.cast(cache.computeIfAbsent(jndiName, n -> {
            try { return new InitialContext().lookup(n); }
            catch (NamingException e) { throw new IllegalStateException(e); }
        }));
    }
}

Take quiz
What does Service Locator cache?
HTTP sessions
SQL result sets only
results of JNDI lookups
compiled JSPs
What has largely replaced Service Locator in modern apps?
dependency injection
static global variables
bigger heap sizes
XML-only configuration

11. What is the Session Facade pattern?

Session Facade puts a coarse-grained service object in front of several fine-grained business objects or entities. A client makes one call, such as placeOrder(), and the facade coordinates inventory, payment and shipping inside a single transaction.

In classic J2EE the facade was a stateless session bean. In Jakarta EE it is usually an @Stateless bean or a Spring @Service with @Transactional.

@Stateless
public class OrderFacade {
    @Inject InventoryService inventory;
    @Inject PaymentService payment;
    @Inject ShippingService shipping;

    public Receipt placeOrder(OrderRequest r) {
        inventory.reserve(r.items());
        var receipt = payment.charge(r.card(), r.total());
        shipping.schedule(r.address(), r.items());
        return receipt;
    }
}

Take quiz
Why use a Session Facade?
to avoid using transactions
to store HTTP cookies
to compile JSP files
to expose one coarse-grained call over many fine-grained services
Where does the transaction usually start in a Session Facade?
inside each JSP
at the facade method boundary
in the browser
in the DAO's constructor

12. What is the Intercepting Filter pattern?

Intercepting Filter lets you run pre- and post-processing around a request without changing the target resource. You chain filters, each one doing a single job: authentication, logging, compression, input sanitising.

In Jakarta EE this is implemented with jakarta.servlet.Filter. Each filter calls chain.doFilter() to pass control onward.

@WebFilter(&quot;/*&quot;)
public class TimingFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        long t = System.nanoTime();
        chain.doFilter(req, res);
        System.out.println(&quot;took &quot; + (System.nanoTime() - t) / 1_000_000 + &quot; ms&quot;);
    }
}

Take quiz
Which method passes control to the next filter?
chain.doFilter(request, response)
response.flushBuffer()
request.getSession()
filter.destroy()
Intercepting Filter is good for:
generating database primary keys
laying out HTML tables
cross-cutting concerns like logging and authentication
compiling Java sources

13. What is the View Helper pattern?

View Helper moves processing logic out of the view and into helper classes so the page only does presentation. A JSP full of scriptlets is hard to read and test. With View Helper, the page calls a tag, a bean or an EL expression and the helper does the work.

JSTL tags, custom tag libraries and Facelets backing beans are all view helpers.

Take quiz
What problem does View Helper solve?
slow database indexes
logic mixed into the view
missing HTTP headers
unsafe class loading
Which is an example of a view helper?
a JDBC PreparedStatement
a ThreadPoolExecutor
a JNDI context
a custom JSP tag or JSTL tag

14. What is the Composite View pattern?

Composite View builds a page from smaller, independent sub-views such as header, menu, content and footer. Each piece can be reused and changed separately, and the layout is assembled at render time.

Apache Tiles, Facelets templates (ui:composition, ui:insert) and JSP includes implement this idea.

Take quiz
Composite View assembles a page from:
database rows only
multiple JVMs
reusable sub-views
compiled servlets only
Which technology offers templating for Composite View in Jakarta Faces?
Facelets
JDBC
JMS
JNDI

15. What is the Value List Handler pattern?

Value List Handler manages large query results on the server and hands the client one page at a time. It keeps a cursor or the query state, so the UI does not pull ten thousand rows into memory.

Today you would usually do this with setFirstResult() and setMaxResults() in JPA, or Spring Data's Pageable.

Take quiz
Value List Handler is used for:
encrypting passwords
routing JMS messages
starting servlets at boot
paging through large result sets
Which modern API replaces it in Spring Data?
Runnable
Pageable
Serializable
Cloneable

16. What is the Observer pattern?

Observer defines a one-to-many link: when a subject changes state, all registered observers are notified automatically. The subject does not know the concrete observers, which keeps them loosely coupled.

Java's old java.util.Observable is deprecated. Use listeners, java.util.concurrent.Flow or CDI events instead.

interface PriceListener { void onPrice(String symbol, double price); }

class PriceFeed {
    private final List<PriceListener> listeners = new CopyOnWriteArrayList<>();
    void subscribe(PriceListener l) { listeners.add(l); }
    void publish(String s, double p) { listeners.forEach(l -> l.onPrice(s, p)); }
}

Take quiz
In Observer, who triggers the notification?
the subject, when its state changes
the garbage collector
the compiler
the web browser
java.util.Observable is:
required by Jakarta EE
the only way to build listeners
deprecated
a Java 21 addition

17. What is the Builder pattern?

Builder constructs a complex object step by step, so you avoid constructors with a dozen parameters. You set only what you need, and build() returns the finished (usually immutable) object.

It also reads well: you see names next to values, which a call like new Order(1, 2, true, null, 5) never gives you.

Order order = Order.builder()
    .customerId(42)
    .item(&quot;SKU-9&quot;, 2)
    .expressShipping(true)
    .build();

Take quiz
What problem does Builder primarily solve?
thread starvation
unwieldy constructors with many optional parameters
slow garbage collection
JNDI lookups
What does build() normally return?
a database connection
the builder itself, unchanged
a new thread
the completed, often immutable object

18. What is the Strategy pattern?

Strategy puts each algorithm behind a common interface so you can pick one at runtime. The client holds a reference to the interface and does not care which implementation runs.

A typical case is shipping cost: flat rate, weight based, or free over a threshold. Instead of a growing if-else block, each rule is a class (or a lambda).

interface ShippingStrategy { double cost(Order o); }

ShippingStrategy flat   = o -> 5.0;
ShippingStrategy weight = o -> o.weightKg() * 1.2;

ShippingStrategy chosen = o.total() > 100 ? (x -> 0.0) : weight;
double fee = chosen.cost(o);

Take quiz
Strategy lets you:
share one instance across all JVMs
avoid writing interfaces
swap algorithms at runtime behind one interface
replace the database
Strategy replaces mainly:
large if-else or switch blocks choosing behaviour
JDBC drivers
web.xml files
thread pools

19. Describe the Proxy pattern?

A Proxy is a stand-in object with the same interface as the real one. It controls access and can add behaviour such as lazy loading, security checks, caching or remote communication before delegating.

Hibernate returns proxies for lazy associations, and Spring wraps beans in proxies to apply @Transactional. In plain Java you can build one with java.lang.reflect.Proxy.

Service real = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
    Service.class.getClassLoader(), new Class<?>[]{Service.class},
    (obj, m, args) -> {
        System.out.println(&quot;before &quot; + m.getName());
        return m.invoke(real, args);
    });

Take quiz
A Proxy shares which thing with the real object?
the same JVM heap address
the same class file name only
the same database row
the same interface
Hibernate uses proxies mainly for:
compiling HQL
lazy loading of associations
opening sockets
signing JARs

20. What is the Template Method pattern?

Template Method fixes the skeleton of an algorithm in a base class and leaves some steps for subclasses to fill in. The order of steps is controlled by the parent and cannot be changed by the child.

A report exporter is a good example: open, fetch data, format, close. Only format() differs between CSV and PDF.

abstract class Exporter {
    public final void export() { open(); var data = fetch(); format(data); close(); }
    protected abstract void format(List<Row> data);
    void open() {} List<Row> fetch() { return List.of(); } void close() {}
}

Take quiz
In Template Method, what is fixed by the base class?
the sequence of steps
the subclass names
the database vendor
the JVM version
Why is the template method often declared final?
to make it run in parallel
to allow serialization
to stop subclasses from changing the step order
to hide it from the compiler

21. How do records simplify DTOs in Java 21?

Records remove the boilerplate you used to write for a DTO: private final fields, constructor, getters, equals, hashCode and toString. One line gives you all of it, and the fields are immutable by default.

You can still validate input with a compact constructor, and records can implement interfaces. They cannot extend another class and they cannot be JPA entities, because JPA needs a no-arg constructor and mutable state. Use them for projections, request/response bodies and events.

public record TransferRequest(String from, String to, BigDecimal amount) {
    public TransferRequest {
        Objects.requireNonNull(from);
        if (amount.signum() <= 0) throw new IllegalArgumentException(&quot;amount must be positive&quot;);
    }
}

Take quiz
Where can a record not be used directly?
as a REST response body
as a standard JPA entity
as a Kafka event payload
as a query projection
What is the compact constructor used for?
extending another class
making fields mutable
declaring static methods only
validating or normalising components

22. How do sealed interfaces improve Strategy and Command?

A sealed interface lists exactly which classes may implement it. With Strategy or Command that gives you a closed set of variants, and the compiler knows all of them.

Combined with pattern matching for switch (final in Java 21), you get exhaustive handling without a default branch. If you add a new command, every switch that misses it stops compiling, which is a bug caught early.

sealed interface PaymentCommand permits Charge, Refund, Cancel {}
record Charge(String id, long cents) implements PaymentCommand {}
record Refund(String id, long cents) implements PaymentCommand {}
record Cancel(String id) implements PaymentCommand {}

String run(PaymentCommand c) {
    return switch (c) {
        case Charge x -> &quot;charged &quot; + x.cents();
        case Refund x -> &quot;refunded &quot; + x.cents();
        case Cancel x   -> &quot;cancelled &quot; + x.id();
    };
}

Take quiz
What do sealed interfaces give you in a switch?
automatic multithreading
faster class loading
compile-time exhaustiveness checks
reflection-free serialization
When you add a new permitted subtype, what happens to an exhaustive switch missing it?
compilation fails
it silently ignores the case
it throws at class load only
it falls back to Object.toString()

23. How does pattern matching for switch replace the Visitor pattern?

Visitor exists because Java could not dispatch on the runtime type of an argument. You added accept(visitor) to every node and a visit method per type. That is a lot of ceremony.

With sealed types, records and record patterns in Java 21 you write one switch and even deconstruct the nested data in the same line. Behaviour stays in one place, and there is no accept method on the model.

sealed interface Expr permits Num, Add, Mul {}
record Num(int v) implements Expr {}
record Add(Expr l, Expr r) implements Expr {}
record Mul(Expr l, Expr r) implements Expr {}

int eval(Expr e) {
    return switch (e) {
        case Num n -> n.v();
        case Add(Expr l, Expr r) -> eval(l) + eval(r);
        case Mul(Expr l, Expr r) -> eval(l) * eval(r);
    };
}

Visitor still makes sense when the type hierarchy is open and third parties add operations, or when you target older Java versions.

Take quiz
Which Java 21 features let you replace many Visitors?
virtual threads and JNDI
string templates and CDI
Serializable and Cloneable
sealed types with pattern matching for switch and record patterns
When might Visitor still be preferred?
when you need exhaustive checks
when the type hierarchy is open and not sealed
when you use records
when you target Java 21 only

24. Why is Singleton risky in clustered J2EE applications?

A Singleton is one instance per class loader per JVM. In a cluster you have many JVMs, so you actually have many "singletons". If one holds mutable state such as a counter or cache, each node sees a different copy and results drift apart.

Hot redeploys add another problem: a new class loader creates a second instance while the old one still lives, which leaks memory. Also, hidden global state makes unit tests order-dependent.

Safer choices: keep Singletons stateless, use the container's @ApplicationScoped or @Singleton bean, and put shared state in a database, Redis or a distributed cache.

Scope What you really get
Plain static Singleton one instance per class loader
CDI @ApplicationScoped one instance per application per JVM
Distributed cache one logical store shared by all nodes

Take quiz
In a cluster, a static Singleton gives you:
one instance per JVM, so several copies overall
exactly one instance across all nodes
one instance per HTTP request
one instance per database row
Where should shared mutable state live in a clustered app?
in a static field
in a servlet instance variable
in an external store such as a database or distributed cache
in a local file on each node

25. How does dependency injection replace Service Locator?

With Service Locator, a class asks a global registry for its dependencies. With dependency injection, the container hands them in through a constructor or field. The class never knows where they came from.

That small shift matters: dependencies become visible in the constructor, tests can pass in fakes without JNDI, and there is no static call to a locator hidden inside methods.

// Locator style: hidden dependency
class Billing {
    void run() { var gw = ServiceLocator.lookup(&quot;java:comp/env/gateway&quot;, Gateway.class); }
}
// DI style: explicit dependency
@ApplicationScoped
class Billing {
    private final Gateway gateway;
    @Inject Billing(Gateway gateway) { this.gateway = gateway; }
}

Take quiz
A key advantage of DI over Service Locator is:
it removes the need for interfaces
dependencies are explicit and easy to replace in tests
it works only with EJBs
it avoids all configuration
In Service Locator, where is the dependency obtained?
in the constructor from the container
by the compiler
from the HTTP request
inside the class by calling a global lookup

26. What is the difference between DAO and Repository?

Both hide persistence, but they speak different languages. A DAO is table-centric: its methods mirror database operations such as insert, update and findByCity. A Repository comes from Domain-Driven Design and acts like an in-memory collection of aggregate roots, using domain terms.

In practice, Spring Data's JpaRepository sits between the two. The important design call is whether callers think in rows or in domain objects.

DAO Repository
Data-centric, one per table or entity Domain-centric, one per aggregate root
Methods like insert/update/delete Methods like add/remove/findActiveCustomers
Core J2EE integration pattern DDD pattern

Take quiz
A Repository is usually organised around:
JDBC connections
servlet mappings
aggregate roots in the domain model
JNDI names
Which pattern traditionally mirrors table operations?
DAO
Repository
Facade
Observer

27. What is the difference between Factory Method and Abstract Factory?

Factory Method creates one product and lets a subclass or method choose the concrete type. Abstract Factory creates a family of related products that must work together, and it hides which family you are using.

Example: a UI toolkit needs a Button and a Checkbox. Abstract Factory guarantees you get both from the Dark theme or both from the Light theme, never a mix.

Factory Method Abstract Factory
Builds a single product Builds a family of related products
Usually one method, often overridden Several factory methods in one interface
Example: NotifierFactory.of(type) Example: ThemeFactory with createButton() and createCheckbox()

Take quiz
Which pattern guarantees that related products are used together?
Factory Method
Singleton
Observer
Abstract Factory
Factory Method typically creates:
a family of products
a single product type
only singletons
only proxies

28. How do virtual threads affect thread-per-request designs?

Virtual threads (final in Java 21, JEP 444) are cheap JVM-managed threads. You can start hundreds of thousands of them, so the simple model of one thread per request works again, even for blocking JDBC or HTTP calls.

That reduces the need for reactive pipelines in many Jakarta EE and Spring apps. You write plain blocking code and the JVM parks the virtual thread while it waits on I/O.

Three habits change: do not pool virtual threads, limit concurrency with a Semaphore if a downstream resource has a cap (such as a connection pool), and avoid long blocking inside synchronized blocks, which can pin the carrier thread in Java 21.

try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
    for (Order o : orders) {
        exec.submit(() -> orderService.process(o));
    }
}

Take quiz
What is the recommended way to use virtual threads?
create one per task, do not pool them
pool them in a fixed-size executor
reuse one for the whole application
convert them to platform threads first
What can pin a virtual thread to its carrier in Java 21?
calling a record accessor
using a switch expression
blocking inside a synchronized block
reading a final field

29. How does Intercepting Filter work with Jakarta Servlet filters?

The container builds a FilterChain for each request from the filters whose URL patterns match. The request enters filter 1, which does its pre-work and calls chain.doFilter(). That moves to filter 2, and so on, until the servlet runs. The response then travels back through the same filters in reverse order.

A filter can also stop the chain by not calling doFilter(), for example to return 401 for a missing token.

sequenceDiagram
    participant C as Client
    participant F1 as AuthFilter
    participant F2 as LogFilter
    participant S as Servlet
    C->>F1: request
    F1->>F2: doFilter
    F2->>S: doFilter
    S-->>F2: response
    F2-->>F1: response
    F1-->>C: response

Order comes from web.xml mapping order, or from registration order in frameworks. Annotation-only filters have no guaranteed order.

Take quiz
How can a filter block a request?
by throwing a checked exception in init()
by not calling chain.doFilter()
by calling System.gc()
by renaming the servlet
In what order does the response pass through filters?
same as the request order
random order
only through the last filter
reverse of the request order

30. How does Spring AOP use the Proxy pattern?

When a bean has an aspect such as @Transactional or @Cacheable, Spring does not give you the raw bean. It gives you a proxy that runs the advice (start transaction, check cache) and then calls the real method.

If the bean implements an interface, Spring uses a JDK dynamic proxy. Otherwise it uses a CGLIB subclass, so the class and its methods cannot be final.

The classic trap is self-invocation: when a method calls another method on this, it bypasses the proxy, so the second method's @Transactional does nothing.

JDK dynamic proxy CGLIB proxy
Needs an interface Subclasses the target class
Proxies only interface methods Cannot override final methods

Take quiz
Why does @Transactional not apply on a self-invoked method?
transactions only work on static methods
Spring disables them for public methods
the call goes to this, bypassing the proxy
the JVM ignores annotations at runtime
Spring uses CGLIB when the bean:
has no interface to proxy
is a record
is a DTO
is declared in XML only

31. How do you implement a thread-safe Singleton?

You have three solid options. The first and simplest is a single-element enum. The second is the initialization-on-demand holder, which is lazy and lock-free because the JVM initialises a class once, safely. The third is double-checked locking with a volatile field, which works but is easy to get wrong.

// Holder idiom
public class Registry {
    private Registry() {}
    private static class Holder { static final Registry INSTANCE = new Registry(); }
    public static Registry get() { return Holder.INSTANCE; }
}

// Double-checked locking
public class Cache {
    private static volatile Cache instance;
    public static Cache get() {
        if (instance == null) {
            synchronized (Cache.class) {
                if (instance == null) instance = new Cache();
            }
        }
        return instance;
    }
}

Without volatile, another thread may see a partially constructed object. In a Jakarta EE app, prefer a container-managed @ApplicationScoped bean over any of these.

Take quiz
Why is volatile needed in double-checked locking?
to make the lock faster
to enable serialization
to allow garbage collection of the class
to prevent seeing a partially constructed instance
Why is the holder idiom lazy and thread-safe?
it uses a synchronized method on every call
the JVM initialises the nested class once, on first use
it relies on reflection
it creates a thread per call

32. What is the difference between Decorator and Proxy?

Both wrap an object and share its interface, so the code looks almost identical. The difference is intent. A Decorator adds new behaviour or responsibilities, and you can stack several. A Proxy controls access to the object (security, lazy creation, remote call) and usually manages the lifecycle of the real subject itself.

Java I/O is the textbook Decorator: new BufferedReader(new InputStreamReader(in)). Hibernate lazy entities and Spring transactional beans are Proxies.

Decorator Proxy
Adds responsibilities Controls access
Client usually builds the stack Proxy often creates or locates the real subject
Stackable Typically one layer

Take quiz
Java's BufferedReader wrapping a Reader is an example of:
Decorator
Proxy
Singleton
Flyweight
The main intent of a Proxy is to:
add stackable extra features
convert one interface to another
control access to the real object
hide a subsystem behind one call

33. When should you use Session Facade?

Use it when a client operation needs several business services or entities to work together in one transaction. A single transferFunds() call is better than the web layer calling debit, credit and audit separately.

It is also right when you want to keep a stable, coarse API for the UI and REST layer while internals change. In remote EJB days it cut network round trips.

Skip it for plain CRUD with one entity, where a facade only forwards calls. And don't dump business rules into the facade. It should orchestrate, while domain objects hold the rules.

Take quiz
A good reason to add a Session Facade is:
to cache static images
one use case spans multiple services in one transaction
to replace the database
to avoid writing tests
What should a Session Facade avoid holding?
transaction boundaries
orchestration of services
a coarse-grained API
detailed domain rules

34. Explain the request lifecycle in Front Controller and MVC?

A request first hits the Front Controller. It applies shared concerns, resolves which controller should handle the URL, and calls it. The controller reads input, calls the service layer, updates the Model, and returns a view name. A view resolver picks the View, which renders the response using model data.

flowchart LR
    A[Browser] --> B["Front Controller"]
    B --> C["Handler Mapping"]
    C --> D[Controller]
    D --> E["Service and DAO"]
    E --> D
    D --> F["Model and View name"]
    F --> G["View Resolver"]
    G --> H["View renders"]
    H --> A

In REST apps the last steps change: the controller returns an object and a message converter writes JSON, so no view resolver is involved.

Take quiz
Which component picks the controller for a URL?
the DAO
the database driver
handler mapping used by the front controller
the view template
In a REST controller, what replaces the view resolver step?
an HTTP message converter writing JSON
a JSP engine
a JDBC row mapper
a thread pool

35. What is the difference between Strategy and State?

The structure is nearly identical: a context delegates to an interface with several implementations. The difference is who changes the implementation and why.

In Strategy, the client picks the algorithm and it usually stays put. In State, the object switches its own behaviour as its state changes, and the states often know which state comes next.

Strategy State
Client selects the algorithm Object changes state internally
Implementations are independent States know about transitions
Example: shipping cost rule Example: order Pending, Paid, Shipped, Cancelled

Take quiz
Who normally decides the implementation in Strategy?
the garbage collector
the previous state
the database
the client
An order moving from Paid to Shipped fits which pattern?
Strategy
State
Builder
Template Method

36. How does the Command pattern support undo and async work?

Command turns a request into an object with an execute() method. Because it is an object you can store it, queue it, log it, retry it, or hand it to another thread.

For undo, each command also knows how to reverse itself (undo()) or keeps a snapshot, and you push executed commands on a stack. For async work, commands are submitted to an ExecutorService, often a virtual-thread one in Java 21.

interface Command { void execute(); void undo(); }

class Deposit implements Command {
    private final Account a; private final long cents;
    Deposit(Account a, long cents) { this.a = a; this.cents = cents; }
    public void execute() { a.add(cents); }
    public void undo()    { a.add(-cents); }
}

Deque<Command> history = new ArrayDeque<>();
Command c = new Deposit(acc, 500);
c.execute(); history.push(c);
history.pop().undo();

Take quiz
Why can Command objects be queued or retried?
the request is an object that can be stored and passed around
they are always static
they run only in the database
they cannot hold state
What data structure typically holds history for undo?
a Set of strings
a single int
a stack of executed commands
a Properties file

37. How do you implement Observer with CDI events?

CDI gives you Observer without writing listener lists. The publisher injects Event<T> and fires an event object. Any bean with a method that has an @Observes parameter of that type is called by the container.

Use fireAsync() with @ObservesAsync for non-blocking delivery. For transaction-aware handling, @Observes(during = TransactionPhase.AFTER_SUCCESS) sends the email only if the order commit worked.

public record OrderPlaced(long orderId) {}

@ApplicationScoped
class OrderService {
    @Inject Event<OrderPlaced> events;
    void place(Order o) { /* save */ events.fire(new OrderPlaced(o.id())); }
}

@ApplicationScoped
class MailListener {
    void on(@Observes(during = TransactionPhase.AFTER_SUCCESS) OrderPlaced e) {
        /* send confirmation */
    }
}

Take quiz
Which annotation marks a CDI observer method parameter?
@Inject
@Observes
@Named
@Produces
What does TransactionPhase.AFTER_SUCCESS achieve?
it rolls back the transaction
it delays JVM shutdown
it makes the event static
the observer runs only if the transaction commits

38. How do Sequenced Collections help build an LRU cache?

Java 21 added SequencedCollection and SequencedMap (JEP 431) with a defined encounter order and methods like firstEntry(), pollFirstEntry(), putLast() and reversed(). LinkedHashMap now implements SequencedMap.

For an LRU cache, the oldest entry sits at the front. On each hit you move the key to the end, and when the size limit is exceeded you remove the first entry. Before Java 21 you needed tricks like removeEldestEntry; now the intent is explicit.

class LruCache<K, V> {
    private final SequencedMap<K, V> map = new LinkedHashMap<>();
    private final int max;
    LruCache(int max) { this.max = max; }

    synchronized V get(K k) {
        V v = map.get(k);
        if (v != null) map.putLast(k, v);   // mark as most recently used
        return v;
    }
    synchronized void put(K k, V v) {
        map.putLast(k, v);
        if (map.size() > max) map.pollFirstEntry();
    }
}

Take quiz
Which method moves an existing key to the most-recent end in a SequencedMap?
pollFirstEntry
reversed
putLast
clear
Which JEP introduced Sequenced Collections?
JEP 431
JEP 444
JEP 440
JEP 441

39. How does the Circuit Breaker pattern work?

A circuit breaker wraps calls to a remote service and counts failures. It has three states. Closed: calls pass through. Open: after too many failures, calls fail immediately without touching the struggling service. Half-open: after a wait time, a few trial calls are allowed, and success closes the circuit again.

stateDiagram-v2
    [*] --> Closed
    Closed --> Open: failure threshold reached
    Open --> HalfOpen: wait time elapsed
    HalfOpen --> Closed: trial calls succeed
    HalfOpen --> Open: trial call fails

It protects threads and connection pools from piling up on a slow dependency. Libraries include Resilience4j and MicroProfile Fault Tolerance's @CircuitBreaker, usually paired with a @Fallback.

Take quiz
In the Open state, calls to the remote service are:
sent normally
queued forever
retried 100 times each
rejected immediately
What does the half-open state do?
shuts down the JVM
allows a few trial calls to test recovery
clears the database
doubles the thread pool

40. How does the Saga pattern manage distributed transactions?

When an operation spans several services with their own databases, a single ACID transaction is not available. A Saga breaks it into a sequence of local transactions. If one step fails, earlier steps are undone by compensating transactions such as refund or release stock.

There are two styles. In choreography, services react to each other's events. In orchestration, a central coordinator tells each service what to do next and triggers compensation on failure.

sequenceDiagram
    participant O as Orchestrator
    participant I as Inventory
    participant P as Payment
    O->>I: reserve stock
    I-->>O: ok
    O->>P: charge card
    P-->>O: failed
    O->>I: release stock (compensate)
Take quiz
What undoes a completed step in a Saga?
a compensating transaction
a two-phase commit
a JVM restart
a database lock
Which Saga style uses a central coordinator?
choreography
serialization
orchestration
delegation

41. Why is Business Delegate rarely used today?

Business Delegate existed to hide JNDI lookups, EJB home interfaces and remote exceptions from the web tier. Modern containers inject local, typed services with @Inject or @Autowired, and remote EJBs are rare, so the delegate would just forward calls.

It still appears where a client talks to a remote API. A small wrapper that adds retry, timeout and error mapping around a REST or gRPC client is a Business Delegate in spirit, even if nobody calls it that.

Take quiz
What did Business Delegate originally hide?
SQL joins
JNDI lookups and remote EJB details
CSS rules
JVM flags
Where does the delegate idea still make sense?
around every local POJO
inside every record
in a static initializer
wrapping a remote API client with retry and error mapping

42. How does Spring JdbcTemplate use Template Method?

JdbcTemplate owns the fixed parts of JDBC work: get a connection, create the statement, execute, convert SQLException into Spring's unchecked exception hierarchy, and release resources. The part that varies, such as binding parameters or mapping a row, is supplied by you through callbacks like RowMapper.

Strictly it is the Template Method idea implemented with callbacks instead of subclassing. You can't forget to close a connection, because you never open one.

List<User> users = jdbcTemplate.query(
    &quot;select id, name from users where city = ?&quot;,
    (rs, i) -> new User(rs.getLong(&quot;id&quot;), rs.getString(&quot;name&quot;)),
    &quot;Charlotte&quot;);

Take quiz
What do you provide to JdbcTemplate for row conversion?
a ServletFilter
a Thread factory
a RowMapper callback
a JNDI context
What fixed work does JdbcTemplate take over?
connection handling, execution and exception translation
HTML rendering
class loading
HTTP routing

43. What is the difference between Adapter and Facade?

An Adapter makes an existing class fit an interface that your code expects. It translates one interface into another, usually wrapping one object. A Facade offers a new, simpler interface over a whole subsystem of many classes.

Example: wrapping a vendor's LegacyPay.makePayment(String, int) to implement your PaymentGateway.charge(Money) is an Adapter. A CheckoutFacade.checkout(cart) that calls pricing, tax, stock and payment is a Facade.

Adapter Facade
Converts interface A to B Simplifies a subsystem
Wraps typically one object Coordinates many objects
Goal: compatibility Goal: ease of use

Take quiz
Which pattern makes an incompatible class fit an expected interface?
Facade
Singleton
Strategy
Adapter
A CheckoutFacade calling pricing, tax and payment is meant to:
convert one interface to another
simplify access to a subsystem
create exactly one instance
stack extra behaviour dynamically

44. How do you implement Chain of Responsibility in Java 21?

Each handler either processes the request or passes it to the next one. The simplest Java 21 style is a functional interface plus a list, so there is no linked-handler boilerplate. A sealed result type can state clearly whether a handler consumed the request.

Servlet filters and the Spring Security filter chain use the same idea.

sealed interface Result permits Handled, Pass {}
record Handled(String message) implements Result {}
record Pass() implements Result {}

interface Handler { Result handle(Request r); }

Result run(List<Handler> chain, Request r) {
    for (Handler h : chain) {
        if (h.handle(r) instanceof Handled done) return done;
    }
    return new Handled(&quot;no handler matched&quot;);
}

Take quiz
In Chain of Responsibility, what does a handler do if it cannot process the request?
passes it to the next handler
throws away the chain
restarts the JVM
logs and closes the server
Which existing Java EE feature behaves like this pattern?
java.lang.Math
String.format
servlet filter chain
Properties.load

45. How does optimistic locking work with JPA @Version?

You add a @Version field to the entity. When you update, JPA issues UPDATE ... SET ..., version = version + 1 WHERE id = ? AND version = ?. If another transaction changed the row first, zero rows match and JPA throws OptimisticLockException (Spring wraps it as ObjectOptimisticLockingFailureException).

No database lock is held while the user thinks, so it scales well for low-conflict workloads. On conflict you reload and retry, or tell the user.

@Entity
public class Account {
    @Id Long id;
    @Version long version;
    BigDecimal balance;
}

Take quiz
What happens when two transactions update the same versioned row?
both succeed silently
the second one fails with OptimisticLockException
the database server restarts
the first is rolled back automatically
Optimistic locking is best for:
workloads where every update conflicts
read-only tables
schemas without primary keys
workloads where conflicts are rare

46. What is the CQRS pattern and when should you use it?

CQRS (Command Query Responsibility Segregation) separates writes from reads. Commands change state and go through the domain model. Queries read from a model shaped for the screen, often a denormalised table or search index.

Use it when read and write loads or shapes differ a lot, for example a heavy dashboard over data written by simple transactions. The cost is complexity and, if the read store is updated asynchronously, eventual consistency.

Don't apply it to a simple CRUD screen. A single model is easier to build and debug.

Side Responsibility
Command Validate, change state, return little or nothing
Query Return data, never change state

Take quiz
In CQRS, queries:
always modify the write model
must use two-phase commit
only read data and do not change state
can only be run at night
A common cost of CQRS with a separate read store is:
eventual consistency
lack of any database
loss of HTTP support
inability to use records

47. What is the difference between Event Sourcing and CRUD?

In CRUD you store the current state and overwrite it on every update. In Event Sourcing you store every state change as an immutable event (OrderPlaced, ItemAdded) and rebuild the current state by replaying them.

You get a full audit trail, the ability to rebuild past states and to create new read models later. You pay with harder queries, event versioning, and a mindset change. It often pairs with CQRS.

CRUD Event Sourcing
Stores latest state Stores sequence of events
Updates overwrite data Events are append-only
History needs extra audit tables History is built in

Take quiz
What does Event Sourcing store?
only the latest row
only HTML pages
only session cookies
an append-only sequence of events
How is the current state obtained in Event Sourcing?
by reading a single overwritten row
by replaying events
by asking the browser
by recompiling the code

48. How do Scoped Values replace ThreadLocal for request context?

ThreadLocal is mutable, lives as long as the thread unless you remove it, and each child thread copies inheritable values. With millions of virtual threads that gets expensive and leak-prone.

ScopedValue is immutable and bound only for the duration of a run() or call() block, then it disappears automatically. In Java 21 it is a preview API (JEP 446), so you need --enable-preview.

static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

void handle(Request r) {
    ScopedValue.where(CURRENT_USER, authenticate(r))
               .run(() -> service.process(r));
}
// inside service.process(): CURRENT_USER.get()

Take quiz
In Java 21, ScopedValue is:
a preview API
removed from the JDK
a finalised API since Java 8
an annotation only
How long is a ScopedValue binding visible?
until the JVM exits
until the thread is garbage collected
only during the run or call block
forever once set

49. Which classic J2EE patterns are obsolete today?

Several patterns existed to work around limits of EJB 2.x, and they have faded. Service Locator and Business Delegate are replaced by injection. Composite Entity and Transfer Object Assembler mostly went away with JPA entities and projections. Value List Handler became paging queries.

Patterns that still matter are Front Controller (every web framework), Intercepting Filter, DAO or Repository, Session Facade as an application service, and Transfer Object, now written as records.

Pattern Today
Service Locator Replaced by CDI or Spring injection
Composite Entity Replaced by JPA entities
Value List Handler Replaced by paging queries
Front Controller Still used, built into frameworks

Take quiz
Which pattern is largely replaced by CDI injection?
Front Controller
Service Locator
Intercepting Filter
Observer
Which classic pattern is still built into modern web frameworks?
Composite Entity
Business Delegate for local calls
Value List Handler with EJB 2 cursors
Front Controller

50. How do you avoid over-engineering with design patterns?

Start with the simplest code that works and introduce a pattern only when a real pain appears: duplicated branches, hard-to-test dependencies, or a class that changes for several reasons. A pattern should remove a problem you can name.

Java 21 helps here. A lambda often replaces a Strategy class, a record replaces a DTO class, and a sealed switch replaces a Visitor. Always ask what the pattern costs in indirection, and whether a teammate can follow the flow in a debugger.

A useful test is to delete the pattern in your head. If the code gets shorter and no requirement is lost, it was not needed.

Take quiz
When should you introduce a design pattern?
on day one for every class
only after deployment
when a concrete problem justifies it
never in Java 21
In Java 21, a simple Strategy can often be replaced by:
a lambda expression
a synchronized block
an abstract class hierarchy of ten levels
a static initializer
«
»

Comments & Discussions