On this page
Proxy
The Proxy pattern provides a surrogate or placeholder that controls access to another object.
Problem
You need to control access to an object — lazy loading, access control, logging, or remote access.
Solution
interface Image {
void display();
}
// Real subject
class HighResolutionImage implements Image {
private final String filename;
HighResolutionImage(String filename) {
this.filename = filename;
loadFromDisk(); // expensive
}
private void loadFromDisk() {
System.out.println("Loading " + filename + " from disk...");
}
public void display() {
System.out.println("Displaying " + filename);
}
}
// Proxy — lazy loading
class ImageProxy implements Image {
private final String filename;
private HighResolutionImage realImage;
ImageProxy(String filename) {
this.filename = filename;
}
public void display() {
if (realImage == null) {
realImage = new HighResolutionImage(filename); // load on first use
}
realImage.display();
}
}
Types of Proxies
| Type | Purpose | Example |
|---|---|---|
| Virtual | Lazy initialization | Load image on first display |
| Protection | Access control | Check permissions before method call |
| Remote | Local representative for remote object | RMI stub |
| Smart reference | Additional actions on access | Reference counting, caching |
Protection Proxy
class SecureDocumentProxy implements Document {
private final Document real;
private final User user;
public void view() {
if (user.hasPermission("READ")) {
real.view();
} else {
throw new AccessDeniedException();
}
}
}
Java Dynamic Proxy
See Dynamic Proxy for JDK dynamic proxies used by Spring AOP and Mockito.
Java Examples
// RMI remote proxy
MyService stub = (MyService) registry.lookup("MyService");
// Collections.synchronizedMap is a protection proxy
Map<String, String> safe = Collections.synchronizedMap(new HashMap<>());
// Spring @Cacheable creates a caching proxy
@Cacheable("users")
public User findById(Long id) { ... }
Proxy vs Decorator
| Proxy | Decorator |
|---|---|
| Controls access | Adds behavior |
| Same interface, may not use all methods | Extends functionality |
| Often manages lifecycle | Wraps existing object |
Best Practices
- Use JDK dynamic proxy for interface-based proxies
- Use CGLIB/ByteBuddy for class-based proxies (Spring default)
- Keep proxy logic thin — delegate to real object after access control
- In Spring, prefer declarative proxies (
@Transactional,@Cacheable) over manual proxies