理解Java动态代理

理解Java动态代理概念理解动态代理类就是在运行时创建的实现了一系列特定接口的类 为了更好行文 还需要先明确三个概念 代理类 在本文中指动态代理类代理接口 在本文中指动态代理类实现的接口代理实例 在本文中指动态代理类的一个实例动态代理的一些特性 代理类是 public 或 final 的 不能是 abstract 代理类均继承自 java lang reflect Proxy 类代理类在创建时按顺序实现了所指定的接

概念理解

动态代理类就是在运行时创建的实现了一系列特定接口的类。

为了更好行文,还需要先明确三个概念:

  1. 代理类——在本文中指动态代理类
  2. 代理接口——在本文中指动态代理类实现的接口
  3. 代理实例——在本文中指动态代理类的一个实例

动态代理的一些特性:

  1. 代理类是publicfinal的,不能是abstract
  2. 代理类均继承自java.lang.reflect.Proxy
  3. 代理类在创建时按顺序实现了所指定的接口
  4. 代理类的名称前缀为$Proxy
  5. 代理类有一个public构造函数,参数为实现了InvocationHandler的对象
  6. 每个代理实例都有一个指定的调用处理器InvocationHandlerInvocationHandler作为其构造函数的参数传入
  7. 代理实例的方法调用会触发InvocationHandler接口的invoke方法

源码简析

Proxy类概览

Proxy类位于java.lang.reflect包中,本文以Android SDK中的Proxy类来分析,与Java SDK中的Proxy可能稍有不同。

Proxy提供了一些静态方法用来生成动态代理类及其实例,同时Proxy类是这些动态代理类的父类。

Proxy的主要变量如下,具体请看注释说明:

/ 代理类名称前缀 */ private final static String proxyClassNamePrefix = "$Proxy"; / 代理类构造函数的参数类型*/ private final static Class[] constructorParams = { InvocationHandler.class }; / 类加载器与对应的代理类缓存映射 */ private static Map 
  
    , Object>> loaderToCache = 
   new WeakHashMap<>(); 
   / 标记某个特定的代理类正在生成 */ 
   private 
   static Object pendingGenerationMarker = 
   new Object(); 
   / next number to use for generation of unique proxy class names */ 
   private 
   static 
   long nextUniqueNumber = 
   0; 
   private 
   static Object nextUniqueNumberLock = 
   new Object(); 
   / 所有代理类的存储集合,在isProxyClass方法中会用到 */ 
   private 
   static Map 
   
     , Void> proxyClasses = Collections.synchronizedMap( 
    new WeakHashMap 
    
      , Void>()); 
     / * 代理类实例的调用处理器 * @serial */ 
     protected InvocationHandler h; 
     
    
  

检查某个特定类是否为动态代理类:

public static boolean isProxyClass(Class 
   cl) { if (cl == null) { throw new NullPointerException(); } return proxyClasses.containsKey(cl); }

可以看到用到了上面定义的proxyClasses

获取代理实例的调用处理器:

public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { /* * Verify that the object is actually a proxy instance. */ if (!(proxy instanceof Proxy)) { throw new IllegalArgumentException("not a proxy instance"); } return ((Proxy) proxy).h; }

Proxy类本身还有一个invoke方法:

private static Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable { InvocationHandler h = proxy.h; return h.invoke(proxy, method, args); }

其他Proxy提供的方法在“动态代理的使用流程分析”一节细说。

InvocationHandler接口

接下来看一下上面多次提到的InvocationHandler接口

/ * {@code InvocationHandler} is the interface implemented by * the invocation handler of a proxy instance. * * 

Each proxy instance has an associated invocation handler. * When a method is invoked on a proxy instance, the method * invocation is encoded and dispatched to the {@code invoke} * method of its invocation handler. */

public interface InvocationHandler { / * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }

注释写的非常清楚,每个代理实例都有一个InvocationHandler对象,实例方法调用时会触发该接口的invoke方法,并返回结果。invoke方法的三个参数分别为代理实例、方法实例(此处的方法实质是代理接口中所定义的方法)、方法参数。

通常我们会自定义一个类实现InvocationHandler接口,如上文示例中的MyInvocationHandler

动态代理的使用流程分析

官方文档给出的生成动态代理类的示例:

// 1.为Foo接口生成动态代理类的步骤 InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler }); // 2.更简单的一种调用方式,实际上是对上面几步的整合 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);

根据上述两种使用方式,我们来跟踪一下源码。

先看getProxyClass

public static Class 
   getProxyClass(ClassLoader loader, Class 
  ... interfaces) throws IllegalArgumentException { return getProxyClass0(loader, interfaces); }

此方法获取一个代理类,参数传入一个类加载器和一个接口数组,最终调到getProxyClass0(ClassLoader loader, Class
... interfaces)
,稍后再来看这个方法的具体实现。

上面得到代理类之后通过getConstructor得到其构造方法,上面说过代理类有一个public构造函数,参数为实现了InvocationHandler的对象,得到构造函数后,然后调用newInstance得到一个代理实例,并将其转为对应的接口对象。

再看第2种简单的实现方式,Proxy.newProxyInstance的实现:

public static Object newProxyInstance(ClassLoader loader, Class 
  [] interfaces, InvocationHandler h) throws IllegalArgumentException { // 先检验调用处理器 if (h == null) { throw new NullPointerException(); } /* * 获取代理类,也是通过调用getProxyClass0 */ Class 
   cl = getProxyClass0(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { // 获取构造函数 final Constructor 
   cons = cl.getConstructor(constructorParams); return newInstance(cons, h); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }

newInstance(cons, h)的实现为:

private static Object newInstance(Constructor 
   cons, InvocationHandler h) { try { // 最终还是走Constructor的newInstance方法 return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException | InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } }

上面两种方式本质都是一样的,只不过第2种方式对用户使用来说更加简单。

上述两种方式的流程很清楚,接下来看最关键的地方,即动态代理类的生成过程,主要是getProxyClass0的实现,其源码较长,具体看注释吧,如下:

private static Class 
   getProxyClass0(ClassLoader loader, Class 
  ... interfaces) { // 校验接口数组的长度 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } Class 
   proxyClass = null; /* collect interface names to use as key for proxy class cache */ // 用于存储代理类实现的所有接口的名称 String[] interfaceNames = new String[interfaces.length]; // for detecting duplicates // 存储不重复的接口类对象 Set 
  
    > interfaceSet = 
   new HashSet<>(); 
   // 循环遍历所有接口 
   for ( 
   int i = 
   0; i < interfaces.length; i++) { 
   /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ 
   // 接口名 String interfaceName = interfaces[i].getName(); Class 
    interfaceClass = 
   null; 
   try { 
   // 通过接口名及类加载器得到一个接口类对象 interfaceClass = Class.forName(interfaceName, 
   false, loader); } 
   catch (ClassNotFoundException e) { } 
   // 校验 
   if (interfaceClass != interfaces[i]) { 
   throw 
   new IllegalArgumentException( interfaces[i] + 
   " is not visible from class loader"); } 
   /* * Verify that the Class object actually represents an * interface. */ 
   // 校验得到的类是否为接口类型 
   if (!interfaceClass.isInterface()) { 
   throw 
   new IllegalArgumentException( interfaceClass.getName() + 
   " is not an interface"); } 
   /* * Verify that this interface is not a duplicate. */ 
   // 校验接口是否重复,不重复的话加入集合 
   if (interfaceSet.contains(interfaceClass)) { 
   throw 
   new IllegalArgumentException( 
   "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); 
   // 存储接口名称到数组 interfaceNames[i] = interfaceName; } 
   /* * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader, and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. */ 
   // 上面已经有英文注释了,简单解释一下:使用字符串作(代理接口的名称)作为代理类缓存的key是足够的,因为我们使用类加载器加载时也是使用名称,使用字符串的另一个优势是它对类是隐式的弱引用。 List 
   
     key = Arrays.asList(interfaceNames); 
    /* * Find or create the proxy class cache for the class loader. */ 
    // 为类加载器查找或创建代理类缓存 Map 
    
      , Object> cache; 
     synchronized (loaderToCache) { cache = loaderToCache.get(loader); 
     if (cache == 
     null) { cache = 
     new HashMap<>(); loaderToCache.put(loader, cache); } 
     /* * This mapping will remain valid for the duration of this * method, without further synchronization, because the mapping * will only be removed if the class loader becomes unreachable. */ } 
     /* * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null, if there is currently no proxy class for the list of * interfaces in the class loader, * the pendingGenerationMarker object, if a proxy class for the * list of interfaces is currently being generated, * or a weak reference to a Class object, if a proxy class for * the list of interfaces has already been generated. */ 
     // 3种查找结果:(1)null(2)pendingGenerationMarker对象(3)一个类对象的弱引用 
     synchronized (cache) { 
     /* * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected, its class loader will have been * garbage collected as well, so the entire cache will be reaped * from the loaderToCache map. */ 
     // 如果一个代理类被GC了,它对应的类加载器也会被GC do { Object value = cache.get(key); 
     // 结果1:返回弱引用,从中得到代理类 
     if (value 
     instanceof Reference) { proxyClass = (Class 
     ) ((Reference) value).get(); } 
     if (proxyClass != 
     null) { 
     // proxy class already generated: return it 
     // 代理类已经生成,并返回 
     return proxyClass; } 
     else 
     if (value == pendingGenerationMarker) { 
     // 结果2:返回pendingGenerationMarker,表示代理类正在生成 
     // proxy class being generated: wait for it 
     try { cache.wait(); } 
     catch (InterruptedException e) { 
     /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } 
     continue; } 
     else { 
     // 结果3:返回null,表示目前还没生成代理类,先在缓存中存储一个标记 
     /* * No proxy class for this list of interfaces has been * generated or is being generated, so we will go and * generate it now. Mark it as pending generation. */ cache.put(key, pendingGenerationMarker); 
     break; } } 
     while ( 
     true); } 
     try { 
     // 代理类的包名 String proxyPkg = 
     null; 
     // package to define proxy class in 
     /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ 
     // 如果为非公有接口,则代理类与代理接口在同一个包内 
     for ( 
     int i = 
     0; i < interfaces.length; i++) { 
     int flags = interfaces[i].getModifiers(); 
     if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); 
     int n = name.lastIndexOf( 
     '.'); 
     // 举个列子,如果name=com.abc.Testinterface,则pkg=com.abc. String pkg = ((n == - 
     1) ? 
     "" : name.substring( 
     0, n + 
     1)); 
     if (proxyPkg == 
     null) { proxyPkg = pkg; } 
     else 
     if (!pkg.equals(proxyPkg)) { 
     throw 
     new IllegalArgumentException( 
     "non-public interfaces from different packages"); } } } 
     if (proxyPkg == 
     null) { 
     // if no non-public proxy interfaces, use the default package. 
     // 默认包名为com.sun.proxy proxyPkg = 
     ""; } { 
     // Android-changed: Generate the proxy directly instead of calling 
     // through to ProxyGenerator. 
     // 请注意上面注释,Android中有变动,直接调用Native方法生成代理类而不是通过ProxyGenerator 
     // 下面方法通过递归获取接口中的所有方法 List 
     
       methods = getMethods(interfaces); 
      // 根据前面及类型排序 Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); 
      // 对方法返回类型进行校验,Throws if any two methods in {@code methods} have the same name and parameters but incompatible return types. validateReturnTypes(methods); 
      // Remove methods that have the same name, parameters and return type. This computes the exceptions of each method; this is the intersection of the exceptions of equivalent methods. List 
      
        []> exceptions = deduplicateAndGetExceptions(methods); 
       // 转成方法数组 Method[] methodsArray = methods.toArray( 
       new Method[methods.size()]); Class 
       [][] exceptionsArray = exceptions.toArray( 
       new Class 
       [exceptions.size()][]); 
       /* * Choose a name for the proxy class to generate. */ 
       final 
       long num; 
       synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } 
       // 生成的动态代理类名,如com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1,com.sun.proxy.$Proxy2 String proxyName = proxyPkg + proxyClassNamePrefix + num; 
       // generateProxy为Native方法 proxyClass = generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); } 
       // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, 
       null); } 
       finally { 
       /* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */ 
       // 代理类生成之后,清除之前Cache中的标记状态,并将其以弱引用的方式保存 
       synchronized (cache) { 
       if (proxyClass != 
       null) { cache.put(key, 
       new WeakReference 
       
         >(proxyClass)); } 
        else { cache.remove(key); } 
        // 唤醒前面cache.wait() cache.notifyAll(); } } 
        // 返回动态代理类 
        return proxyClass; } 
        
       
      
     
    
  

动态代理示例

示例1

先定义一个代理接口,描述如何”上网冲浪“:

package com.aspook.dynamicproxy2; public interface SurfInternet { 
    void surf(String content); } 

再定义一个委托类:

public class Normal implements SurfInternet { 
    @Override public void surf(String content) { System.out.println("上网: " + content); } }

创建一个自定义的InvocationHandler

public class VPNInvocationHandler implements InvocationHandler { 
    private SurfInternet base; public VPNInvocationHandler(SurfInternet obj) { base = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke start"); // 会调用委托类的对应方法,从而实现代理 method.invoke(base, args); System.out.println("invoke end"); return null; } }

测试类:

public class Test { 
    public static void main(String[] args) { // 被代理类,或叫做委托类 Normal normal = new Normal(); InvocationHandler h = new VPNInvocationHandler(normal); SurfInternet vpn = (SurfInternet) Proxy.newProxyInstance(normal.getClass().getClassLoader(), normal.getClass().getInterfaces(), h); vpn.surf("VPN方式,可以访问Google了"); } }

输出结果:

注意第2行的输出,实质上是被代理类的功能,代理类一般用于不想将被代理类暴露出去,而是转用代理类来实现被代理类的功能,即所谓的代理。在上面的例子有一个被代理类Normal,与静态代理模式相比(本质上是代理类持有一个被代理类的实例,代理类与被代理类均实现相同接口),动态代理在运行时才会生成。

示例2

先定义一个代理接口:

package com.aspook.dynamicproxy; public interface HelloService { void sayHello(String content); } 

注意此接口为public,位于com.aspook.dynamicproxy包下,按上文分析,生成的动态代理类应该位于默认包中,包名为com.sun.proxy,稍后验证。

再来实现一个逻辑类ProxyClassTest,具体请看注释:

public class ProxyClassTest { 
    private InvocationHandler h; // 初始化一个InvocationHandler public void prepareInvocationHandler() { h = new MyInvocationHandler(); } // 只有一个代理接口示例 public 
  
    T 
   create(Class 
   
     service) { 
    return (T) Proxy.newProxyInstance(service.getClassLoader(), 
    new Class[]{service}, h); } 
    // 这里有两个代理接口,GoodService代表另一个接口,源码未给出 
    public 
    
      T 
     create() { 
     return (T) Proxy.newProxyInstance(HelloService.class.getClassLoader(), 
     new Class[]{HelloService.class, GoodService.class}, h); } 
     // 代理实例方法调用触发invoke回调后可执行的个人逻辑 
     private 
     void 
     doLogic(String firstArg) { System.out.println(firstArg); } 
     // InvocationHandler的实现类,当然也可以用匿名内部类 class MyInvocationHandler implements InvocationHandler { 
     @Override 
     public Object 
     invoke(Object proxy, Method method, Object[] args) 
     throws Throwable { System.out.println( 
     "invoke"); System.out.println( 
     "proxy: " + proxy.getClass()); System.out.println( 
     "method: " + method.getName()); System.out.println( 
     "args length: " + args.length); 
     // 由于只是示例,仅供说明,因此下面两行未做越界校验 System.out.println( 
     "args : " + args[ 
     0].toString()); doLogic(args[ 
     0].toString()); 
     // 注意返回值要跟接口方法返回值相对应 
     return 
     null; } } } 
     
    
  

最后看下调用类:

public class Test { 
    public static void main(String[] args) { ProxyClassTest test = new ProxyClassTest(); test.prepareInvocationHandler(); HelloService service = test.create(); service.sayHello("hahaha"); } }

最终输入结果如下:

由输出结果可以看到代理全路径名称为com.sun.proxy.$Proxy0,跟前文中的理论一致,同时可以得到方法名和方法参数,并可执行自定义逻辑。

此示例并没有实现被代理类,多个接口的响应均统一在invoke中处理,然后实现具体逻辑,而不必像静态代理那样单独实现每一个接口。Retrofit也是利用了此原理。

注意事项

  1. 如果代理接口有多个,如下面代码有HelloServiceGoodService两个接口
    public 
        
          T 
         create() { 
         return (T) Proxy.newProxyInstance(HelloService.class.getClassLoader(), 
         new Class[]{HelloService.class, GoodService.class}, h); } 
        

    只要有一个接口为非public的,则生成的动态代理类就跟接口同包,如com.aspook.dynamicproxy.$Proxy0

  2. 如果多个接口返回类型不同,可以在invoke中根据方法的返回类型来return不同的值。
  3. 如果跨包引用一个代理接口,则该接口必然为public的,如果只有这一个接口则动态代理类位于默认包中;如果还有其他同包的代理接口,只要有一个为非public的,则生成的动态代理类就跟非public的接口同包,如果无论跨包同包所有接口均为public,则生成的动态代理类在默认包中。
  4. 由于动态代理类本身已经继承了Proxy,根据Java单继承的限制,因此只能实现多个接口了。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210330.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午7:49
下一篇 2026年3月19日 上午7:49


相关推荐

  • MySQL时区问题_docker设置时区

    MySQL时区问题_docker设置时区使用mysql-connector-java-8.0.9-rc出现以下异常:解决方案:在url后面添加&amp;serverTimezone=GMT%2B8,如:url:jdbc:mysql://localhost:3306/activiti?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serve…

    2025年6月29日
    4
  • python定义全局变量

    python定义全局变量第一 如定义在类或者函数体外 在函数或者类中引用需要用到 global 声明 temp t ceshi deftmp1 globaltemp ttemp t 1printtemp t 结果 1 第二 直接在函数或类中定义 需要先执行函数或者类 才能执行输出 defa globalcccc bb defb globalcccc kk a b

    2026年3月20日
    2
  • 软件测试——数据库_藤藤

    软件测试——数据库_藤藤

    2026年3月14日
    4
  • Git规范:Git提交规范

    Git规范:Git提交规范1 Commitmessag 格式 type scope subject 1 type 必须 作用 用于说明 Gitcommit 的类别 只允许使用下面的标识 feat 新功能 feature fix to 修复 bug 可以是 QA QualityAssur 发现的 BUG 也可以是研发自己发现的 BUG 备注 fix 产生 diff 并自动修复此问题 适合于一次提交直接修复问题 to 只产生 diff 不自动修复此问题 subject scope type

    2025年11月5日
    5
  • ❤️肝下25万字的《决战Linux到精通》笔记,你的Linux水平将从入门到入魔❤️【建议收藏】[通俗易懂]

    ❤️肝下25万字的《决战Linux到精通》笔记,你的Linux水平将从入门到入魔❤️【建议收藏】[通俗易懂]文章目录操作系统的发展史UnixMinixLinux操作系统的发展Minix没有火起来的原因Linux介绍Linux内核&发行版Linux内核版本Linux发行版本类Unix系统目录结构Linux目录用户目录命令行基本操作命令使用方法查看帮助文档helpman(manual)tab键自动补全history游览历史命令行中的ctrl组合键Linux命令权限管理列出目录的内容:ls显示inode的内容:stat文件访问权限修改文件权限:chmod修改文件所有者:chown修改文件所属组:chgrp文件.

    2022年6月1日
    24
  • Matlab矩阵复制扩充

    考虑这个问题:定义一个简单的行向量a    如何复制10行呢?即:    同理,对于一个列向量,如何复制10列呢?  关键函数1:repmat(A,m,n):将向量/矩阵在垂直方向复制m次,在水平方向复制n次。      再举一个例子,对于a=[12;34]:         垂直方向复制3次,水平方向复制2次,

    2022年4月8日
    63

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号