动态代理

简介: 动态代理

一.概念

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制

动态代理的方式:

  • JDK 自身提供的动态代理,利用反射机制
  • cglib 动态代理,基于 ASM

## 二.场景

  • 面向切面的编程(AOP)
  • RPC 调用

三.代理解决的问题

首先,它是一个代理机制。如果熟悉设计模式中的代理模式,我们会知道,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成。通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用,通过代理,可以提供更加友善的界面。还可以通过代理,可以做一个全局的拦截器。

四.使用

1.jdk 的动态代理

I.使用

publicinterfaceAnimal {

   voideat();

}

publicclassCatimplementsAnimal {

   @Override

   publicvoideat() {

       System.out.println("The cat is eating");

   }

}

public class Dog implements Animal {

   @Override

   public void eat() {

       System.out.println("The dog is eating");

   }

}

public class AnimalProxy implements InvocationHandler {

   private Object target; // 代理对象


   public Object getInstance(Object target) {

       this.target = target;

       // 取得代理对象

       return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

   }


   @Override

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

       System.out.println("调用前");

       Object result = method.invoke(target, args); // 方法调用

       System.out.println("调用后");

       return result;

   }

}

public class Test {

   public static void main(String[] args) {

       // JDK 动态代理调用

       AnimalProxy proxy = new AnimalProxy();

       Animal dogProxy = (Animal) proxy.getInstance(new Dog());

       dogProxy.eat();

   }

}

  • JDK Proxy 是通过实现 InvocationHandler 接口来实现的
  • 通过 newProxyInstance 获取到代理类对象
  • 重写 invoke 方法,以便代理类调用
  • JDK Proxy 只能代理实现接口的类

II.为什么 JDK Proxy 只能代理实现接口的类?

查看 newProxyInstance 的源码

/**

* @param   loader the class loader to define the proxy class

* @param   interfaces the list of interfaces for the proxy class to implement

*/

@CallerSensitive

public static Object newProxyInstance(ClassLoader loader,

                                     Class<?>[] interfaces,

                                     InvocationHandler h)

 throws IllegalArgumentException

{

 Objects.requireNonNull(h);

       final Class<?>[] intfs = interfaces.clone();

       final SecurityManager sm = System.getSecurityManager();

       if (sm != null) {

           checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

       }

       /*

        * Look up or generate the designated proxy class.

        */

       Class<?> cl = getProxyClass0(loader, intfs);

    //省略其他代码

}

private static Class<?> getProxyClass0(ClassLoader loader,

                                      Class<?>... interfaces) {

 if (interfaces.length > 65535) {

   throw new IllegalArgumentException("interface limit exceeded");

 }


 // If the proxy class defined by the given loader implementing

 // the given interfaces exists, this will simply return the cached copy; 缓存

 // otherwise, it will create the proxy class via the ProxyClassFactory  代理类工厂

 return proxyClassCache.get(loader, interfaces);

}

public V get(K key, P parameter) {

 Objects.requireNonNull(parameter);//校验接口列表是否为null

 expungeStaleEntries();

 Object cacheKey = CacheKey.valueOf(key, refQueue);

 // lazily install the 2nd level valuesMap for the particular cacheKey

 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);

 if (valuesMap == null) {

   ConcurrentMap<Object, Supplier<V>> oldValuesMap

     = map.putIfAbsent(cacheKey,

                       valuesMap = new ConcurrentHashMap<>());

   if (oldValuesMap != null) {

     valuesMap = oldValuesMap;

   }

 }

 // create subKey and retrieve the possible Supplier<V> stored by that

 // subKey from valuesMap

 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

 Supplier<V> supplier = valuesMap.get(subKey);

 Factory factory = null;

 while (true) {

   if (supplier != null) {

     // supplier might be a Factory or a CacheValue<V> instance

     V value = supplier.get();

     if (value != null) {

       return value;

     }

   }

   // else no supplier in cache

   // or a supplier that returned null (could be a cleared CacheValue

   // or a Factory that wasn't successful in installing the CacheValue)

   // lazily construct a Factory

   if (factory == null) {//没有缓存的时候,工厂也不存在,需要通过接口列表信息创建Factory

     factory = new Factory(key, parameter, subKey, valuesMap);

   }

   if (supplier == null) {

     supplier = valuesMap.putIfAbsent(subKey, factory);

     if (supplier == null) {

       // successfully installed Factory

       supplier = factory;

     }

     // else retry with winning supplier

   } else {

     if (valuesMap.replace(subKey, supplier, factory)) {

       // successfully replaced

       // cleared CacheEntry / unsuccessful Factory

       // with our Factory

       supplier = factory;

     } else {

       // retry with current supplier

       supplier = valuesMap.get(subKey);

     }

   }

 }

}

  • loader:为类加载器,也就是 target.getClass().getClassLoader()
  • interfaces:接口代理类的接口实现列表

根据注释说明,这个问题的源头,在于 JDK Proxy 的源码设计。首先会判断接口列表集合是否为空,为空直接抛出异常,不为空,如果缓存中不存在且工厂类也不存在,会通过接口列表信息创建工厂类.

如果要执意动态代理,非接口实现类就会报错.

2.cglib 动态代理

JDK 动态代理机制只能代理实现了接口的类,Cglib 是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理。

I.引入包

<dependency>

   <groupId>cglib</groupId>

   <artifactId>cglib</artifactId>

   <version>3.2.9</version>

</dependency>

II.代码实现

public class Panda {

   public void eat() {

       System.out.println("The panda is eating");

   }

}

public class CglibProxy implements MethodInterceptor {

   private Object target; // 代理对象


   public Object getInstance(Object target) {

       this.target = target;

       Enhancer enhancer = new Enhancer();

       // 设置父类为实例类

       enhancer.setSuperclass(this.target.getClass());

       // 回调方法

       enhancer.setCallback(this);

       // 创建代理对象

       return enhancer.create();

   }


   @Override

   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

       System.out.println("调用前");

       Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用

       System.out.println("调用后");

       return result;

   }

}

public class Test {

   public static void main(String[] args) {

       // CGLIB 动态代理调用

       CglibProxy proxy = new CglibProxy();

       Panda panda = (Panda) proxy.getInstance(new Panda());

       panda.eat();

   }

}

  • cglib 的调用通过实现 MethodInterceptor 接口的 intercept 方法;
  • 调用 invokeSuper 进行动态代理的;
  • 可以直接对普通类进行动态代理;

五.对比

JDK Proxy 的优势

  • 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,更加可靠;
  • 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版上能够使用;

Cglib 框架的优势

  • 可调用普通类,不需要实现接口;
  • 高性能;
  • 内部复杂,需要借助 Cglib 包,底层是 asm 字节码插桩技术
目录
打赏
0
0
0
0
4
分享
相关文章
kde
|
5天前
|
Docker镜像加速指南:手把手教你配置国内镜像源
配置国内镜像源可大幅提升 Docker 拉取速度,解决访问 Docker Hub 缓慢问题。本文详解 Linux、Docker Desktop 配置方法,并提供测速对比与常见问题解答,附最新可用镜像源列表,助力高效开发部署。
kde
3152 8
国内如何安装和使用 Claude Code镜像教程 - Windows 用户篇
国内如何安装和使用 Claude Code镜像教程 - Windows 用户篇
572 0
Dify MCP 保姆级教程来了!
大语言模型,例如 DeepSeek,如果不能联网、不能操作外部工具,只能是聊天机器人。除了聊天没什么可做的。
842 9
2025年最新版最细致Maven安装与配置指南(任何版本都可以依据本文章配置)
本文详细介绍了Maven的项目管理工具特性、安装步骤和配置方法。主要内容包括: Maven概述:解释Maven作为基于POM的构建工具,具备依赖管理、构建生命周期和仓库管理等功能。 安装步骤: 从官网下载最新版本 解压到指定目录 创建本地仓库文件夹 关键配置: 修改settings.xml文件 配置阿里云和清华大学镜像仓库以加速依赖下载 设置本地仓库路径 附加说明:包含详细的配置示例和截图指导,适用于各种操作系统环境。 本文提供了完整的Maven安装和配置
2025年最新版最细致Maven安装与配置指南(任何版本都可以依据本文章配置)
【保姆级图文详解】大模型、Spring AI编程调用大模型
【保姆级图文详解】大模型、Spring AI编程调用大模型
359 7
【保姆级图文详解】大模型、Spring AI编程调用大模型
Excel数据治理新思路:引入智能体实现自动纠错【Python+Agent】
本文介绍如何利用智能体与Python代码批量处理Excel中的脏数据,解决人工录入导致的格式混乱、逻辑错误等问题。通过构建具备数据校验、异常标记及自动修正功能的系统,将数小时的人工核查任务缩短至分钟级,大幅提升数据一致性和办公效率。
DeepSeek R1+Open WebUI实现本地知识库的搭建和局域网访问
本文介绍了使用 DeepSeek R1 和 Open WebUI 搭建本地知识库的详细步骤与注意事项,涵盖核心组件介绍、硬件与软件准备、模型部署、知识库构建及问答功能实现等内容,适用于本地文档存储、向量化与检索增强生成(RAG)场景的应用开发。
369 0
让AI时代的卓越架构触手可及,阿里云技术解决方案开放免费试用
阿里云推出基于场景的解决方案免费试用活动,新老用户均可领取100点试用点,完成部署还可再领最高100点,相当于一年可获得最高200元云资源。覆盖AI、大数据、互联网应用开发等多个领域,支持热门场景如DeepSeek部署、模型微调等,助力企业和开发者快速验证方案并上云。
306 22
让AI时代的卓越架构触手可及,阿里云技术解决方案开放免费试用
FLUX.1 Kontext 的全生态教程来啦!AIGC专区在线试玩!
Flux.1 Kontext [dev] 开源模型大家都用上了吗?小编汇总了3个使用教程,打包送上!
426 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问