Understanding Java Proxy Pattern: Static, JDK Dynamic, and CGLIB Proxies
This article explains the proxy design pattern in Java, demonstrates how to create static proxies, uses JDK dynamic proxies with InvocationHandler, and shows how CGLIB can generate class‑based proxies, highlighting their implementations, usage examples, and limitations.
The proxy pattern is a common software design pattern that provides a surrogate or placeholder for another object to control access to it, such as network connections, large objects in memory, files, or other resources.
First, a static proxy example is presented: an IUserProvider interface is defined, implemented by UserProviderImpl , and a logging proxy LogProxy implements the same interface, delegating calls to the real provider while adding simple log statements.
package com.fun.ztest.proxytest;
public interface IUserProvider {
User getUser(int i);
} package com.fun.ztest.proxytest;
public class LogProxy implements IUserProvider {
private static Logger logger = LoggerFactory.getLogger(LogProxy.class);
private IUserProvider userProvider;
public LogProxy(IUserProvider userProvider) { this.userProvider = userProvider; }
@Override
public User getUser(int i) {
logger.info("获取用户{}", i + EMPTY);
return userProvider.getUser(i);
}
}The static proxy works but is tightly coupled to the IUserProvider interface, limiting reuse.
To address this, JDK dynamic proxies are introduced. The AutoLogProxy class implements InvocationHandler , intercepting method calls, logging them, and delegating to the underlying target object.
public class AutoLogProxy implements InvocationHandler {
private static Logger logger = LoggerFactory.getLogger(AutoLogProxy.class);
private final Object drive;
public AutoLogProxy(Object invocationTarget) { this.drive = invocationTarget; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.warn("调用方法:{},参数:{}", method.getName(), Arrays.toString(args));
return method.invoke(drive, args);
}
}Usage involves Proxy.newProxyInstance with the target object and the handler, allowing the proxy to be created at runtime for any interface.
IUserProvider userProvider = new UserProviderImpl();
IUserProvider provider = (IUserProvider) Proxy.newProxyInstance(
IUserProvider.class.getClassLoader(),
new Class[]{IUserProvider.class},
new AutoLogProxy(userProvider)
);
provider.getUser(10);Dynamic proxies, however, require an interface; they cannot proxy classes directly. For class‑based proxying, the CGLIB library is used.
A CGLIB example defines a concrete class CUser with methods fun and tester , and a CUserProxy that implements MethodInterceptor to log method invocations before delegating to the original method.
public class CUser extends SourceCode {
public void fun() { output(DEFAULT_STRING); }
public void tester() { output(DEFAULT_CHARSET.name()); }
} public class CUserProxy implements MethodInterceptor {
private static Logger logger = LoggerFactory.getLogger(CUserProxy.class);
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
logger.info("调用方法:{},参数:{}", method.getName(), Arrays.toString(args));
proxy.invokeSuper(obj, args);
return obj;
}
}Using CGLIB’s Enhancer , a proxy instance of CUser is created, and method calls are automatically logged.
CUserProxy proxy = new CUserProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CUser.class);
enhancer.setCallback(proxy);
CUser user = (CUser) enhancer.create();
user.fun();The article concludes that the proxy pattern is powerful for adding cross‑cutting concerns without modifying existing code, and that dynamic proxies (JDK or CGLIB) provide reusable and flexible ways to implement such behavior.
FunTester
10k followers, 1k articles | completely useless
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.