DevNotes
Support
Essential Design Patterns · Chapter 07

Proxy Pattern

Controlling access to another object — the bank-account protection proxy, kinds of proxies (protection, virtual, remote, caching, logging), dynamic proxies, and how Spring AOP, @Transactional and Hibernate lazy loading use proxies.

◷ 5 min read§ 11 sections? 6 interview questions{ } 5 code examples

#Introduction

Purpose: control access to another object.

A proxy implements the same interface as the real object and stands in front of it. Clients talk to the proxy exactly as they would to the real object, while the proxy decides whether, when and how to forward each call.

#Example: Bank Account

Subject and Real Subject

Java
interface BankAccount {
    void withdraw(double amount);
}

class RealBankAccount implements BankAccount {
    @Override
    public void withdraw(double amount) {
        System.out.println("Withdrawn " + amount);
    }
}

The Proxy

Java
class BankAccountProxy implements BankAccount {
    private final RealBankAccount account = new RealBankAccount();
    private final boolean authenticated;

    public BankAccountProxy(boolean authenticated) {
        this.authenticated = authenticated;
    }

    @Override
    public void withdraw(double amount) {
        if (!authenticated) {
            System.out.println("Access Denied");
            return;
        }
        account.withdraw(amount);
    }
}

Client

Java
public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccountProxy(true);
        account.withdraw(5000);

        BankAccount unauthorized = new BankAccountProxy(false);
        unauthorized.withdraw(5000);
    }
}
Output
Withdrawn 5000.0
Access Denied

(The amount is a double, so it prints as 5000.0.)

#Structure

Client
Proxy — same interface, adds control
Real object
RoleIn the exampleResponsibility
SubjectBankAccountInterface shared by proxy and real object
Real subjectRealBankAccountDoes the actual work
ProxyBankAccountProxyControls access, then delegates

#Kinds of Proxies

KindWhat it controlsExample
Protection proxyPermissionsThe bank example; Spring Security's method security (@PreAuthorize)
Virtual proxyExpensive creation — create the real object lazily on first useHibernate lazy-loading proxies
Remote proxyNetwork access — a local object representing a remote oneRMI stubs, Feign clients, gRPC stubs
Caching proxyRepeated work — return cached resultsSpring @Cacheable
Logging / monitoring proxyCross-cutting concerns — timing, auditing, metricsSpring AOP aspects
Smart referenceReference counting, locking, transactionsSpring @Transactional

A Virtual (Lazy) Proxy

Java
class LazyReport implements Report {
    private RealReport real;          // expensive to build
    private final long id;

    LazyReport(long id) { this.id = id; }

    @Override
    public String content() {
        if (real == null) {
            real = RealReport.load(id);   // built only when actually needed
        }
        return real.content();
    }
}

#Dynamic Proxies

Writing a proxy class per interface doesn't scale. Java can generate proxies at runtime (see Dynamic Proxies in the Reflection topic):

Java
BankAccount real = new RealBankAccount();

BankAccount proxy = (BankAccount) Proxy.newProxyInstance(
        BankAccount.class.getClassLoader(),
        new Class<?>[] { BankAccount.class },
        (p, method, args) -> {
            System.out.println("Before " + method.getName());
            Object result = method.invoke(real, args);
            System.out.println("After " + method.getName());
            return result;
        });

proxy.withdraw(100);
JDK dynamic proxyCGLIB / ByteBuddy proxy
Requires an interfaceSubclasses the concrete class
Built into the JDKLibrary (bundled in Spring)
Only interface methods are proxiedCan't proxy final classes or final/private methods

#Proxies in Spring and Hibernate

Caller
Spring proxy — transaction / security / cache / async
Your bean
  • @Transactional — the proxy begins a transaction, calls your method, then commits or rolls back.
  • @Cacheable — the proxy checks the cache before calling your method.
  • @Async — the proxy submits the call to an executor.
  • @PreAuthorize — the proxy checks permissions first.
  • Hibernate lazy loading — order.getCustomer() returns a proxy that loads the customer on first access (and throws LazyInitializationException if the session is closed).

#Proxy vs Decorator vs Adapter

All three wrap another object; the intent differs:

ProxyDecoratorAdapter
InterfaceSame as the real objectSame as the wrapped objectDifferent — converts one interface to another
IntentControl accessAdd behaviourMake incompatible interfaces work together
Who creates the wrapped objectOften the proxy itself, or the frameworkThe client, stacking decoratorsThe client or configuration
StackingNot typicalDesigned to stackNot typical

#When to Use

  • Access control, lazy creation, remote access, caching, logging or transactions — without changing the real class.
  • Adding cross-cutting behaviour transparently to many classes (via dynamic proxies / AOP).

#Interview Questions

Q1What is the Proxy pattern?

A structural pattern that provides a substitute implementing the same interface as another object, to control access to it.

Q2What types of proxies are there?

Protection (access control), virtual (lazy creation), remote (represents a remote object), caching, and logging/smart-reference proxies.

Q3How does Spring use proxies?

Spring AOP wraps beans in JDK dynamic proxies or CGLIB subclasses to apply @Transactional, @Cacheable, @Async, method security and custom aspects.

Q4Why doesn't @Transactional work when a method calls another method of the same class?

Because the internal call goes through this, not through the proxy, so the proxy's transaction logic never runs.

Q5What's the difference between Proxy and Decorator?

Both implement the same interface as the wrapped object. A proxy controls access (and often manages the real object's lifecycle); a decorator adds behaviour and is designed to be stacked by the client.

Q6JDK dynamic proxy vs CGLIB?

JDK proxies require an interface and implement it at runtime; CGLIB creates a subclass of the concrete class, so it works without interfaces but can't proxy final classes or methods.

#Common Mistakes

  • Self-invocation in Spring beans bypassing the proxy.
  • final classes or methods on beans that need class-based proxies.
  • Accessing Hibernate lazy proxies outside a session.
  • Confusing proxy with decorator in interviews.

#Key Takeaways

  • A proxy has the same interface as the real object and controls access to it.
  • Protection, virtual, remote, caching and logging proxies serve different purposes.
  • Dynamic proxies generate proxies at runtime — the basis of Spring AOP.
  • @Transactional, @Cacheable, @Async and Hibernate lazy loading are proxies.
  • Self-invocation bypasses the proxy.