Proxy Pattern
Provide a surrogate or placeholder for another object to control access to it. The proxy and the real object implement the same interface.
Proxy = a bouncer at a club. Same door (interface), but the bouncer decides who gets in (access control), checks IDs (protection proxy), or tells you 'the DJ is setting up' until the real party starts (virtual/lazy proxy).
Four common proxy types: (1) Virtual proxy — lazy-initializes an expensive object (loads an image only when first rendered); (2) Protection proxy — checks access rights before delegating; (3) Remote proxy — represents a remote object (RMI stub, gRPC stub); (4) Caching proxy — caches results of expensive operations. Proxy is the structural backbone of AOP: Spring's @Transactional generates a dynamic proxy that wraps your service method with transaction management.
In Java, dynamic proxies (java.lang.reflect.Proxy and CGLIB) generate proxy classes at runtime without writing boilerplate. Spring and Hibernate depend on this heavily. The internal mechanics: CGLIB creates a subclass of the proxied class and overrides methods; JDK dynamic proxy creates a proxy for interfaces only using reflection and an InvocationHandler. Proxy vs Decorator: structurally identical — both wrap an object with the same interface. Intent separates them: Decorator enriches behaviour; Proxy controls access, adds cross-cutting concerns (logging, security, transaction), or delays initialization. Proxy is also used for the Null Object pattern (a no-op proxy that implements the interface with empty implementations).
I use proxy most often for cross-cutting concerns without modifying the target class. A LoggingProxy implements the same service interface, wraps the real service, and logs every method call's duration and result. The client gets the proxy injected — it never knows. Lazy loading is another scenario: a HeavyReportProxy holds no data until the first call to getReport() — then it loads from the DB, caches, and returns. I always clarify: Proxy keeps the same interface and controls access; Decorator keeps the same interface and adds behaviour. The line is intent, not structure.
JDK dynamic proxy only proxies interfaces — it cannot proxy a concrete class directly. CGLIB subclasses the concrete class, but final classes and final methods cannot be proxied. Know which Spring beans are not AOPable (final classes, @Configuration with proxyBeanMethods=false).