尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Java代理模式详解:静态、JDK动态与CGLIB实现对比

发布时间:2026/9/15 5:39:02

资讯中心
01
ARTICLE

Java代理模式详解:静态、JDK动态与CGLIB实现对比

Java代理模式详解:静态、JDK动态与CGLIB实现对比
1. 代理模式为什么我们需要它在Java开发中我们经常会遇到这样的场景你无法直接访问某个对象或者想在访问对象时添加一些额外操作。这时候代理模式Proxy Pattern就派上用场了。作为一个在Java领域摸爬滚打多年的开发者我发现代理模式是面试中最常被问到的设计模式之一也是实际项目中最实用的模式之一。代理模式的核心思想很简单为其他对象提供一个代理或占位符以控制对这个对象的访问。听起来有点抽象让我用一个生活中的例子来解释假设你想邀请一位明星来参加你的活动你不会直接去找明星本人而是会联系他的经纪人。这个经纪人就是明星的代理他可以帮助处理各种事务比如筛选邀请、安排行程等。在Java中代理模式主要解决以下几个问题控制对原始对象的访问比如权限校验在访问对象时添加额外功能比如日志记录延迟加载当对象创建开销很大时简化复杂对象的访问方式2. 代理模式的三种实现方式2.1 静态代理最直观的实现静态代理是最简单直接的代理实现方式。它需要你手动创建一个代理类这个代理类和被代理类实现相同的接口。// 接口 public interface Image { void display(); } // 真实对象 public class RealImage implements Image { private String fileName; public RealImage(String fileName) { this.fileName fileName; loadFromDisk(); } private void loadFromDisk() { System.out.println(Loading fileName); } Override public void display() { System.out.println(Displaying fileName); } } // 代理类 public class ImageProxy implements Image { private RealImage realImage; private String fileName; public ImageProxy(String fileName) { this.fileName fileName; } Override public void display() { if (realImage null) { realImage new RealImage(fileName); } realImage.display(); } }静态代理的优点实现简单容易理解不侵入原有代码可以在不修改目标对象的情况下扩展功能但静态代理也有明显的缺点每个代理类只能为一个接口服务如果有多个接口需要代理就需要创建多个代理类接口一旦增加方法代理类和目标对象都要修改2.2 JDK动态代理运行时生成代理类JDK动态代理是Java内置的代理机制它可以在运行时动态创建代理类。这种方式不需要我们手动编写代理类而是由JVM在运行时生成。import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicProxyHandler implements InvocationHandler { private Object target; public DynamicProxyHandler(Object target) { this.target target; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(Before method method.getName()); Object result method.invoke(target, args); System.out.println(After method method.getName()); return result; } public static Object createProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new DynamicProxyHandler(target) ); } }使用示例Image realImage new RealImage(test.jpg); Image proxyImage (Image) DynamicProxyHandler.createProxy(realImage); proxyImage.display();JDK动态代理的特点只能代理实现了接口的类生成的代理类继承自Proxy类通过InvocationHandler接口实现方法调用拦截性能比静态代理稍差但更加灵活提示JDK动态代理在实际项目中常用于AOP面向切面编程实现比如Spring框架中的事务管理、日志记录等。2.3 CGLIB动态代理无需接口的代理CGLIBCode Generation Library是一个强大的高性能代码生成库它可以在运行时扩展Java类和实现接口。与JDK动态代理不同CGLIB不需要目标对象实现接口。import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { private Object target; public CglibProxy(Object target) { this.target target; } public Object getProxyInstance() { Enhancer enhancer new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(Before method method.getName()); Object result method.invoke(target, args); System.out.println(After method method.getName()); return result; } }使用示例RealImage realImage new RealImage(test.jpg); RealImage proxyImage (RealImage) new CglibProxy(realImage).getProxyInstance(); proxyImage.display();CGLIB代理的特点可以代理没有实现接口的类通过继承方式实现代理性能比JDK动态代理更好不能代理final类和方法3. 代理模式的应用场景3.1 远程代理Remote Proxy远程代理用于在不同的地址空间中代表对象。典型的例子是RPC远程过程调用框架客户端持有的实际上是一个代理对象这个代理对象负责与远程服务器通信。// 伪代码示例 public class RemoteImageProxy implements Image { private String fileName; private String host; private int port; public RemoteImageProxy(String fileName, String host, int port) { this.fileName fileName; this.host host; this.port port; } Override public void display() { // 通过网络与远程服务器通信 Socket socket new Socket(host, port); ObjectOutputStream out new ObjectOutputStream(socket.getOutputStream()); out.writeObject(display); out.writeObject(fileName); // 处理响应... } }3.2 虚拟代理Virtual Proxy虚拟代理用于延迟加载开销大的对象。比如在加载大图片时可以先显示一个缩略图等真正需要时才加载完整图片。public class ImageProxy implements Image { private RealImage realImage; private String fileName; private ImageIcon icon; public ImageProxy(String fileName) { this.fileName fileName; // 先加载缩略图 this.icon new ImageIcon(thumbnails/ fileName); } Override public void display() { if (realImage null) { // 当真正需要时才加载大图 realImage new RealImage(fileName); } realImage.display(); } }3.3 保护代理Protection Proxy保护代理用于控制对原始对象的访问权限。比如在文件系统中不同用户对文件的操作权限不同。public class ProtectedFileProxy implements File { private RealFile realFile; private User user; public ProtectedFileProxy(RealFile realFile, User user) { this.realFile realFile; this.user user; } Override public void read() { if (user.hasReadPermission()) { realFile.read(); } else { throw new SecurityException(No read permission); } } Override public void write() { if (user.hasWritePermission()) { realFile.write(); } else { throw new SecurityException(No write permission); } } }3.4 智能引用代理Smart Reference Proxy智能引用代理在访问对象时执行额外的操作比如记录对象被引用的次数当对象没有被引用时自动释放资源锁定对象以确保其他对象不能修改它public class SmartReferenceProxy implements Image { private RealImage realImage; private int referenceCount 0; public SmartReferenceProxy(RealImage realImage) { this.realImage realImage; } Override public void display() { referenceCount; System.out.println(Reference count: referenceCount); realImage.display(); } public void close() { if (--referenceCount 0) { System.out.println(No more references, releasing resource); realImage.close(); } } }4. 代理模式的性能考量与最佳实践4.1 性能比较在实际项目中选择哪种代理实现方式需要考虑性能因素代理类型创建速度执行速度内存占用适用场景静态代理快快低接口稳定代理逻辑简单JDK动态代理慢中等中等需要代理多个接口CGLIB最慢最快高需要代理类而非接口4.2 最佳实践根据我的项目经验以下是使用代理模式的一些最佳实践接口优先尽量让被代理的类实现接口这样你可以选择使用JDK动态代理它比CGLIB更轻量级。避免过度使用代理模式会引入额外的间接层不要为了使用模式而使用模式只有在真正需要控制访问或添加额外功能时才使用。关注线程安全如果你的代理类有状态比如缓存确保它是线程安全的。考虑性能影响在性能敏感的场景中代理调用的开销可能变得显著需要进行性能测试。清晰的命名代理类应该清楚地表明它们的角色比如UserServiceProxy、RemoteFileProxy等。4.3 常见问题与解决方案问题1动态代理为什么只能代理接口JDK动态代理这是因为JDK动态代理生成的代理类已经继承了Proxy类而Java不支持多重继承所以无法再继承其他类。解决方案是使用CGLIB它通过生成目标类的子类来实现代理。问题2CGLIB代理final方法的问题CGLIB无法代理final方法因为它是通过继承实现的。解决方案是避免在需要被代理的类中使用final方法将这些方法提取到另一个类中使用组合而非继承问题3代理导致的栈溢出当代理方法中又调用了被代理对象的其他方法时可能会导致无限递归Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里又调用了method可能导致无限递归 return method.invoke(target, args); }解决方案是直接调用目标方法而非通过反射Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 直接调用目标对象的方法 return target.doSomething(); }5. 代理模式在流行框架中的应用5.1 Spring AOP中的代理Spring框架广泛使用代理模式来实现AOP功能。根据目标对象是否实现接口Spring会自动选择使用JDK动态代理还是CGLIB。Aspect Component public class LoggingAspect { Before(execution(* com.example.service.*.*(..))) public void logBefore(JoinPoint joinPoint) { System.out.println(Before method: joinPoint.getSignature().getName()); } }Spring代理的特点默认使用JDK动态代理如果目标对象实现接口可以通过配置强制使用CGLIB代理对象由Spring容器管理5.2 MyBatis中的Mapper代理MyBatis通过JDK动态代理为Mapper接口创建实现类public class MapperProxyT implements InvocationHandler { private final SqlSession sqlSession; private final ClassT mapperInterface; public MapperProxy(SqlSession sqlSession, ClassT mapperInterface) { this.sqlSession sqlSession; this.mapperInterface mapperInterface; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 将方法调用转换为SQL执行 return sqlSession.selectOne(method.getName(), args[0]); } }5.3 Hibernate中的延迟加载Hibernate使用代理模式实现延迟加载Lazy Loading。当你访问一个关联对象时Hibernate返回的是一个代理对象只有在真正访问其属性时才会从数据库加载数据。Entity public class Order { Id private Long id; ManyToOne(fetch FetchType.LAZY) private Customer customer; // ... } // 使用时 Order order entityManager.find(Order.class, 1L); Customer customer order.getCustomer(); // 这里返回的是代理对象 String name customer.getName(); // 此时才会真正加载数据6. 代理模式的变体与扩展6.1 链式代理Proxy Chain有时候我们需要对同一个方法执行多个代理操作这时可以使用链式代理public class ProxyChain implements InvocationHandler { private ListInvocationHandler handlers new ArrayList(); private Object target; private int index 0; public ProxyChain(Object target) { this.target target; } public void addHandler(InvocationHandler handler) { handlers.add(handler); } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (index handlers.size()) { return handlers.get(index).invoke(proxy, method, args); } return method.invoke(target, args); } }6.2 动态代理的缓存优化由于创建动态代理对象开销较大我们可以使用缓存来优化public class ProxyFactory { private static final MapClass?, Object proxyCache new ConcurrentHashMap(); SuppressWarnings(unchecked) public static T T createProxy(T target) { return (T) proxyCache.computeIfAbsent( target.getClass(), k - Proxy.newProxyInstance( k.getClassLoader(), k.getInterfaces(), new LoggingInvocationHandler(target) ) ); } }6.3 接口默认方法的代理处理Java 8引入了接口默认方法这给动态代理带来了一些挑战。我们需要特别处理默认方法Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.isDefault()) { // 处理默认方法 return InvocationHandler.invokeDefault(proxy, method, args); } // 正常处理 return method.invoke(target, args); }在实际项目中我发现代理模式是构建灵活、可扩展系统的强大工具。它不仅能帮助我们实现横切关注点的分离还能在不修改原有代码的情况下增强功能。掌握各种代理实现方式的优缺点能够让你在架构设计时做出更合理的选择。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。