常见的Proxy源码分析

文章目录

      • 一 描述
      • 二 Javassist
      • 三 Jdk Proxy
      • 四 Dubbo的Proxy


一 描述

dubbo的源码中,还有两个关于javassist技术和jdk proxy的扩展,
javassist:动态编程,通过jdk中的JavaCompiler接口,能够直接对一个已经存在java class文件进行编译,也可以在内存中动态生成java代码,动态编译执行
jdk proxy:jdk动态代理,通过Proxy类动态的根据指定的接口生成一个class byte,该class会继承自Proxy类,并且实现你的所有指定的接口,然后再利用指定的classloader将class byte加载进jvm,最后生成一个类的对象,并初始化该类的一些值,以及所有的接口和方法,这样客户端就能拿到你所定义的接口的一个Proxy对象

二 Javassist

Javassist是一个强调源代码层面进行操作增加的字节码操作工具
利用Javassist实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单,直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构或者动态。
其中最重要的是ClassPoolCtClassCtMethodCtField这四个类:

  • CtClass(compile-time
    class):编译时类信息,它是一个class文件在代码中的抽象表现形式,可以通过一个类的全限定名来获取一个CtClass对象,用来表示这个类文件。
  • ClassPool:从开发视角来看,ClassPool是一张保存CtClass信息的HashTable,key为类名,value为类名对应的CtClass对象。当我们需要对某个类进行修改时,就是通过pool.getCtClass(“className”)方法从pool中获取到相应的CtClass。
  • CtMethod、CtField:这两个比较好理解,对应的是类中的方法和属性。

以一个简单的小示例来说明一下javassist相关API的使用:
这里,我们以增强Base类的process()方法,在方法的执行之前打印start,在方法的执行之后打印end

/**
 * @author: zhoucg
 * @date: 2019-10-16
 */
public class Base {

    public void process(){
        System.out.println("process");
    }
}

增强类的定义JavassistTest

/**
 *
 * 利用Javassist实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单
 * 其中最重要的是ClassPool、CtClass、CtMethod、CtField这四个类:
 *
 * CtClass(compile-time class):编译时类信息,它是一个class文件在代码中的抽象表现形式,
 *                               可以通过一个类的全限定名来获取一个CtClass对象,用来表示这个类文件。
 * ClassPool:从开发视角来看,ClassPool是一张保存CtClass信息的HashTable,key为类名,value为类名对应的CtClass对象。当我们需要对某个类进行修改时,
 *            就是通过pool.getCtClass(“className”)方法从pool中获取到相应的CtClass。
 *
 * CtMethod、CtField:这两个比较好理解,对应的是类中的方法和属性。
 *
 * @author: zhoucg
 * @date: 2019-10-16
 */
public class JavassistTest {

    public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
        ClassPool cp = ClassPool.getDefault();

        CtClass cc = cp.get("com.common.jdk.javassist.Base");
        CtMethod m = cc.getDeclaredMethod("process");

        m.insertBefore("{ System.out.println(\"start\"); }");
        m.insertAfter("{ System.out.println(\"end\"); }");

        Class c = cc.toClass();

        Base h = (Base)c.newInstance();
        h.process();
    }
}

三 Jdk Proxy

dubbo中的Proxy类对于指定接口的动态类的class byte的生成是使用了javassist技术,而jdk 原生的JDK Proxy对于指定接口的动态类的class byte的生成是使用ProxyGenerator进行生成的,对于动态类的的对象的创建是由内部的ProxyClassFactory创建的
常见的Proxy源码分析_第1张图片
JDK原生的Proxy类中,很明显,公共的方法就红框中的四个,受保护的构造函数一个,
1,protected Proxy(InvocationHandler h),传参是一个InvocationHandler对象,这个对象的作用,它是Proxy代理实例调用处理程序实现的一个接口,每一个proxy代理实例都有一个调用关联的处理程序,在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。(这个看一个代理类之后就能明白)
2,public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException 静态的方法,获取对应的代理类的InvocationHandler对象

public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
    {
        /*
         * Verify that the object is actually a proxy instance.
         */
         //1,首先是判断对应的proxy是否为代理类
        if (!isProxyClass(proxy.getClass())) {
            throw new IllegalArgumentException("not a proxy instance");
        }
		//为对应的代理类时,直接获取代理类中的h变量
        final Proxy p = (Proxy) proxy;
        final InvocationHandler ih = p.h;
        if (System.getSecurityManager() != null) {
            Class ihClass = ih.getClass();
            Class caller = Reflection.getCallerClass();
            if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
                                                    ihClass.getClassLoader()))
            {
                ReflectUtil.checkPackageAccess(ihClass);
            }
        }

        return ih;
    }

3,public static Class getProxyClass(ClassLoader loader,
Class… interfaces) throws IllegalArgumentException
根据指定的ClassLoader和被代理的Class对象数组,获取对应的代理类Class对象
4,public static boolean isProxyClass(Class cl) 判读当前的Class字节码类是否为对应的代理
5,public static Object newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHandler h) throws IllegalArgumentException 根据对应的ClassLoader,被代理类的接口Class数组,InvocationHandler对象获取对应的代理对象

/**
 * @author: zhoucg
 * @date: 2019-05-17
 */
public interface ProxyBean {

    void say();
}

例如我们对应ProxyBean去创建它的代理类

//获取到代理对象的Class对象
Class proxyClass = Proxy.getProxyClass(ProxyBean.class.getClassLoader(),ProxyBean.class);
//根据Proxy.newProxyInstance(ClassLoader,Class[] interfaces,InvocationHandler)获取代理对象
ProxyBean proxyClassBean = (ProxyBean) Proxy.newProxyInstance(proxyClass.getClassLoader(),proxyClass.getInterfaces(),myInvocationHandler);
proxyClassBean.say();
//根据获取到的代理的Class对象实例化
Constructor constructors  = proxyClass.getConstructor(InvocationHandler.class);
System.out.println("aa"+constructors);
ProxyBean constructorProxyBean = (ProxyBean) constructors.newInstance(myInvocationHandler);
constructorProxyBean.say();

MyInvocationhHandler

/**
 * @author: zhoucg
 * @date: 2019-05-17
 */
public class MyInvocationHandler implements InvocationHandler {

    private final TestAddBean testAddBean;

    public MyInvocationHandler(TestAddBean testAddBean) {
        this.testAddBean = testAddBean;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        testAddBean.test(methodName);
        return null;
    }
}

生成的$Proxy

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.common.util.proxy.jdk.ProxyBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements ProxyBean {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

   
    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.common.util.proxy.jdk.ProxyBean").getMethod("say", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

在ProxyBean接口的代理类中,首先是继承了JDK 的Proxy类,然后实现类ProxyBean接口,并实现类ProxyBean接口中的say()方法,

public final void say() throws  {
    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

say方法中,将对应的调用委托给了之前传递进入的MyInvocationHandler对象的invoke(Object obj,Method method,Object[] args)方法进行指定,可以发现,第一个参数代表的就是当前接口ProxyBean的代理类,第二个参数method表示的是指定的mehod对象,第三个参数表示方法的参数信息,

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //加上这个
    System.out.println(proxy);
    String methodName = method.getName();
    testAddBean.test(methodName);
    return null;
}

很明显,这个是会报错的,System.out.println(proxy);方法实际上就是指定System.out.println(proxy.toString()); 方法,所以会进入到$Proxy方法的toString()方法,但是,toString还是会将方法的指定委托给handler中的invoke方法,这个就形成了一个死循环了
所以:不要视图在invoke方法中去对代理类进行toString(),hash(),equals()方法

Exception in thread "main" java.lang.StackOverflowError
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
 at java.io.PrintStream.println(PrintStream.java:821)
 at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
 at com.sun.proxy.$Proxy0.toString(Unknown Source)
 at java.lang.String.valueOf(String.java:2994)
。。。。。。。。。。。。。

四 Dubbo的Proxy

dubbo对于proxy的使用,实际上相对于jdk 的 Proxy简单的多,对于动态类的class byte的生成使用了javassist技术,【dubbo的ClassGenerator源码】

/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.common.bytecode;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;

import com.alibaba.dubbo.common.utils.ClassHelper;
import com.alibaba.dubbo.common.utils.ReflectUtils;

/**
 * Proxy.
 * 
 * @author qian.lei
 */

public abstract class Proxy
{
   private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);

   private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();

   public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler(){
      public Object invoke(Object proxy, Method method, Object[] args){ return null; }
   };

   public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler(){
      public Object invoke(Object proxy, Method method, Object[] args){ throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented."); }
   };

   private static final Map> ProxyCacheMap = new WeakHashMap>();

   private static final Object PendingGenerationMarker = new Object();

   /**
    * Get proxy.
    * 
    * @param ics interface class array.
    * @return Proxy instance.
    */
   public static Proxy getProxy(Class... ics)
   {
      return getProxy(ClassHelper.getCallerClassLoader(Proxy.class), ics);
   }

   /**
    * Get proxy.
    * @param cl class loader.
    * @param ics interface class array.
    * 
    * @return Proxy instance.
    */
   public static Proxy getProxy(ClassLoader cl, Class... ics)
   {
      if( ics.length > 65535 )
         throw new IllegalArgumentException("interface limit exceeded");
      
      StringBuilder sb = new StringBuilder();
      for(int i=0;i tmp = null;
         try
         {
            tmp = Class.forName(itf, false, cl);
         }
         catch(ClassNotFoundException e)
         {}

         if( tmp != ics[i] )
            throw new IllegalArgumentException(ics[i] + " is not visible from class loader");

          sb.append(itf).append(';');
      }

      // use interface class name list as key.
      String key = sb.toString();

      // get cache by class loader.
      Map cache;
      synchronized( ProxyCacheMap )
      {
         cache = ProxyCacheMap.get(cl);
         if( cache == null )
          {
            cache = new HashMap();
            ProxyCacheMap.put(cl, cache);
          }
      }

      Proxy proxy = null;
      synchronized( cache )
      {
         do
         {
            Object value = cache.get(key);
            if( value instanceof Reference )
            {
               proxy = (Proxy)((Reference)value).get();
               if( proxy != null )
                  return proxy;
            }

            if( value == PendingGenerationMarker )
            {
               try{ cache.wait(); }catch(InterruptedException e){}
            }
            else
            {
               cache.put(key, PendingGenerationMarker);
               break;
            }
         }
         while( true );
      }

      long id = PROXY_CLASS_COUNTER.getAndIncrement();
      String pkg = null;
      ClassGenerator ccp = null, ccm = null;
      try
      {
         ccp = ClassGenerator.newInstance(cl);

         Set worked = new HashSet();
         List methods = new ArrayList();

         for(int i=0;i rt = method.getReturnType();
               Class[] pts = method.getParameterTypes();

               StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
               for(int j=0;j[]{ InvocationHandler.class }, new Class[0], "handler=$1;");
            ccp.addDefaultConstructor();
         Class clazz = ccp.toClass();
         clazz.getField("methods").set(null, methods.toArray(new Method[0]));

         // create Proxy class.
         String fcn = Proxy.class.getName() + id;
         ccm = ClassGenerator.newInstance(cl);
         ccm.setClassName(fcn);
         ccm.addDefaultConstructor();
         ccm.setSuperClass(Proxy.class);
         ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
         Class pc = ccm.toClass();
         proxy = (Proxy)pc.newInstance();
      }
      catch(RuntimeException e)
      {
         throw e;
      }
      catch(Exception e)
      {
         throw new RuntimeException(e.getMessage(), e);
      }
      finally
      {
         // release ClassGenerator
         if( ccp != null )
            ccp.release();
         if( ccm != null )
            ccm.release();
         synchronized( cache )
         {
            if( proxy == null )
               cache.remove(key);
            else
               cache.put(key, new WeakReference(proxy));
            cache.notifyAll();
         }
      }
      return proxy;
   }

   /**
    * get instance with default handler.
    * 
    * @return instance.
    */
   public Object newInstance()
   {
      return newInstance(THROW_UNSUPPORTED_INVOKER);
   }

   /**
    * get instance with special handler.
    * 
    * @return instance.
    */
   abstract public Object newInstance(InvocationHandler handler);

   protected Proxy(){}

   private static String asArgument(Class cl, String name)
   {
      if( cl.isPrimitive() )
      {
         if( Boolean.TYPE == cl )
            return name + "==null?false:((Boolean)" + name + ").booleanValue()";
         if( Byte.TYPE == cl )
            return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
         if( Character.TYPE == cl )
            return name + "==null?(char)0:((Character)" + name + ").charValue()";
         if( Double.TYPE == cl )
            return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
         if( Float.TYPE == cl )
            return name + "==null?(float)0:((Float)" + name + ").floatValue()";
         if( Integer.TYPE == cl )
            return name + "==null?(int)0:((Integer)" + name + ").intValue()";
         if( Long.TYPE == cl )
            return name + "==null?(long)0:((Long)" + name + ").longValue()";
         if( Short.TYPE == cl )
            return name + "==null?(short)0:((Short)" + name + ").shortValue()";
         throw new RuntimeException(name+" is unknown primitive type."); 
      }
      return "(" + ReflectUtils.getName(cl) + ")"+name;
   }
}

你可能感兴趣的:(中间件)