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