What is the Proxy design pattern, and what does it look like in Java?
answer
- Same interface as real subject = transparent stand-in
- Intent = control access (vs Decorator = add behavior)
- Kinds: virtual / protection / remote / smart
- Static (hand-written forwarder) vs dynamic (reflect.Proxy)
- Powers Spring AOP, Hibernate lazy load, RMI, mocks
basics
~20 sA 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 sThe 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
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.
Distinguishes the proxy kinds (virtual/protection/remote/smart), can write a static proxy, and explains that proxy shares the subject's interface for transparency.
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).
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)