skip to content

What is the Proxy design pattern, and what does it look like in Java?

level: juniorimportance: must knowfreq 70%

answer

  1. Same interface as real subject = transparent stand-in
  2. Intent = control access (vs Decorator = add behavior)
  3. Kinds: virtual / protection / remote / smart
  4. Static (hand-written forwarder) vs dynamic (reflect.Proxy)
  5. Powers Spring AOP, Hibernate lazy load, RMI, mocks

basics

~20 s

A proxy is a stand-in object that has the same interface as a real object. Calls go to the proxy first, which can add behavior (security checks, logging, lazy loading) and then forward to the real object.

solid answer

~40 s

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. The proxy implements the same interface as the real subject, so callers can't tell the difference; this lets the proxy intercept every call and add logic before/after delegating. In Java you implement it either statically (write a class that holds a reference to the target and forwards each method) or dynamically (java.lang.reflect.Proxy plus an InvocationHandler, which builds a proxy class at runtime). Common kinds are virtual proxies (lazy creation of expensive objects), protection proxies (access control), remote proxies (stand-in for a remote object), and smart proxies (logging, reference counting). The key intent is controlling access; the proxy and subject share a type so the proxy is transparent to clients.

go deeper

for a junior

Can define a proxy as a stand-in with the same interface that forwards calls and adds a little logic, and name an example like access checks or lazy loading.

for a middle

Distinguishes the proxy kinds (virtual/protection/remote/smart), can write a static proxy, and explains that proxy shares the subject's interface for transparency.

for a senior

Cleanly contrasts Proxy vs Decorator vs Adapter by intent, knows static vs dynamic proxies, and ties the pattern to real framework usage (Spring AOP, Hibernate lazy load, RMI).

for a principal

Reasons about where to put cross-cutting concerns via proxies, the trade-offs of interface vs subclass proxying, self-invocation pitfalls, and the performance/debuggability cost of pervasive proxying in a system.

## The problem Sometimes you don't want callers to talk to an object directly. Maybe the object is expensive to create and you want to defer that until it's actually used; maybe you need a security check before any method runs; maybe the object lives on another machine; maybe you want to log or cache every call. In all these cases you want something that *looks exactly like* the real object but sits in front of it and **controls access** to it. ## The pattern The **Proxy pattern** introduces a *surrogate* (the proxy) that implements the **same interface** as the *real subject*. Because they share an interface, a caller holding the interface type cannot tell whether it has the proxy or the real thing — the proxy is **transparent**. Every method call lands on the proxy, which can do work *before* and *after* forwarding (delegating) the call to the real subject. Three roles: - **Subject** — the shared interface (e.g. `Image`). - **RealSubject** — the actual implementation (e.g. `HighResImage`). - **Proxy** — implements `Subject`, holds a reference to the `RealSubject` (or creates it lazily), and forwards calls. ## Kinds of proxy - **Virtual proxy** — delays creating an expensive real object until a method is first called (lazy loading). Example: don't load a huge image from disk until `display()` is called. - **Protection proxy** — checks permissions before delegating; throws or returns early if the caller isn't allowed. - **Remote proxy** — local stand-in for an object that actually lives in another JVM/process/machine; the proxy marshals the call across the network (the classic example is Java RMI stubs). - **Smart proxy / smart reference** — adds bookkeeping: logging, caching, reference counting, lazy locking. ## Two ways to build it in Java **1. Static (hand-written) proxy.** You write a class that implements the interface, holds the target, and forwards each method explicitly. Simple and explicit, but you must write one forwarding method per interface method, and a new proxy class per interface. ```java class LoggingService implements Service { private final Service target; LoggingService(Service target) { this.target = target; } public String fetch(int id) { System.out.println("calling fetch(" + id + ")"); return target.fetch(id); } } ``` **2. Dynamic proxy.** `java.lang.reflect.Proxy.newProxyInstance(...)` *generates* a proxy class at runtime that implements a given set of interfaces. Every method call on it is routed to a single `InvocationHandler.invoke(proxy, method, args)` method, where you write the cross-cutting logic once and delegate via `method.invoke(target, args)`. This avoids hand-writing a forwarder per method and is the basis of most framework interception (Spring AOP's JDK-proxy mode, mocking libraries, transaction/security interceptors). Its one constraint: JDK dynamic proxies can only proxy **interfaces**, not concrete classes (libraries like CGLIB/ByteBuddy subclass instead, to proxy classes). ## Proxy vs Decorator Both wrap an object that shares the wrapped type, so they look structurally identical. The difference is **intent**: a **Decorator adds new behavior/responsibilities** to an object (and you typically stack several), while a **Proxy controls access** to an object (existence, permissions, location) and usually wraps exactly one target whose lifecycle it may even own. A decorator's client knowingly composes behaviors; a proxy is meant to be invisible. ## Why it matters Proxies are the mechanism behind huge parts of the Java ecosystem: Spring's declarative `@Transactional`/`@Async`/`@Cacheable`/`@PreAuthorize`, Hibernate lazy-loaded entity associations, RMI stubs, and most mock objects are all proxies intercepting calls and adding behavior around a real (or fake) target.

  • How is a Proxy different from a Decorator if both wrap an object of the same type?
    Structurally they're nearly identical, but the intent differs: a Decorator adds responsibilities/behavior and is usually stacked, while a Proxy controls access (lazy creation, permissions, remoting) and is meant to be transparent, often owning the single target's lifecycle.
  • Give a concrete example of a virtual proxy.
    An image viewer that holds a lightweight ImageProxy. The real high-resolution image is only loaded from disk the first time display() is called, so opening a document with many images is fast and memory is used only for images actually shown.

saying these in an interview costs you the question

  • Confusing Proxy with Adapter — Adapter changes the interface, Proxy keeps the same interface
  • Saying Proxy and Decorator are the same; they share structure but differ in intent (access control vs added behavior)
  • Thinking a proxy must always hold an already-created target — a virtual proxy creates it lazily
  • Claiming JDK dynamic proxies can proxy any class (they can only proxy interfaces)

context