creational
Proxy
Provide a surrogate or placeholder for another object to control access to it.
Sometimes you want to do something extra whenever an object is used. Load it only when first needed. Cache the results. Check permissions. Log the call.
Putting that logic inside the object mixes two concerns. Putting it in every caller repeats it everywhere.
A proxy is a stand-in. It implements the same interface as the real object, so callers cannot tell the difference. It does the extra work, then passes the call along.
A credit card is a proxy for your bank account — same 'pay' interface but adds authorization and logging.
Key Concepts
1
The proxy implements the same interface as the real object and holds a reference to it.
2
Each method does its extra job first, then forwards the call. A lazy proxy creates the real object on first use. A caching proxy returns a stored result when it can. A protection proxy checks permissions before forwarding.
3
Spring uses this heavily. @Transactional and @Cacheable work by wrapping your bean in a proxy.
@Transactional@Cacheable
4
That explains a bug people hit often. If a method calls another method on the same object directly, the call never leaves the object. It never passes through the proxy, so the annotation does nothing.
When to use it
- Virtual Proxy — lazy initialization
- Protection Proxy — check permissions
- Caching Proxy — cache expensive results
- Logging Proxy — audit all calls
Watch out for
- An interface that looks local but is remote, lazy or cached invites callers to assume performance and failure characteristics that are not true
- Caching and lazy-loading proxies need explicit thread-safety; a naive lazy proxy will initialise twice under concurrency
- Dynamic proxies (JDK or CGLIB) are how Spring implements @Transactional and @Cacheable — which is exactly why a self-invocation inside the same bean silently skips the proxy and the annotation does nothing
java
public interface YouTubeLib {
List<Video> listVideos();
Video getVideoInfo(String id);
}
public class CachedYouTubeProxy implements YouTubeLib {
private final YouTubeService service;
private List<Video> listCache;
private final Map<String, Video> videoCache = new HashMap<>();
public List<Video> listVideos() {
if (listCache == null) listCache = service.listVideos();
return listCache;
}
public Video getVideoInfo(String id) {
return videoCache.computeIfAbsent(id, service::getVideoInfo);
}
}