加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

深入理解 Java 动态代理机制 java的动态代理机制详解

发布时间:2020-12-15 07:09:34 所属栏目:Java 来源:网络整理
导读:Java 有两种代理方式,一种是静态代理,另一种是动态代理。对于静态代理,其实就是通过依赖注入,对对象进行封装,不让外部知道实现的细节。很多 API 就是通过这种形式来封装的。 代理模式结构图(图片来自《大话设计模式》) 下面看下两者在概念上的解释:

Java 有两种代理方式,一种是静态代理,另一种是动态代理。对于静态代理,其实就是通过依赖注入,对对象进行封装,不让外部知道实现的细节。很多 API 就是通过这种形式来封装的。

代理模式结构图(图片来自《大话设计模式》)

下面看下两者在概念上的解释:

静态代理

  • 静态代理类:由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。

  • 静态代理类通常只代理一个类。

  • 静态代理事先知道要代理的是什么。

动态代理

  • 动态代理类:在程序运行时,通过反射机制动态生成。

  • 动态代理类通常代理接口下的所有类。

  • 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。

  • 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。

  • Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。

动态代理的好处

Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。此外,也可以减少代码量,如果采用静态代理,类的方法比较多的时候,得手写大量代码。

动态代理实例

静态代理的实例这里就不说了,比较简单。在 java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口可以生成 JDK 动态代理类和动态代理对象。下面讲讲动态代理的实现。

先定义一个接口:

public interface Person {
    void setName(String name);
}

再定义一个学生 Student 类来实现 Person 接口,每一个学生都有一个自己的名字:

class Student implements Person {
    
    private String mName;

    public Student(String name) {
        mName = name;
    }

     setName(String name) {
        mName = name;
    }
}

?Student 类中,定义了一个私有变量 mName,用来表示?Student 的名字。接下去定义一个代理 handler,就是用来帮我们处理代理的 :

class PersonHandler<T>  InvocationHandler {
    // 代理的目标对象
     T mTarget;

     PersonHandler(T target) {
        mTarget = target;
    }

    @Override
    public Object invoke(Object o,Method method,Object[] objects) throws Throwable {
         调用开始前的操作
        ProxyUtil.start();
         调用方法,通过反射的形式来调用 mTarget 的 method 
        Object result = method.invoke(mTarget,objects);
         打印改名前的名字
        ProxyUtil.log(objects[0].toString());
         调用结束后的操作
        ProxyUtil.finish();
        return result;
    }
}

可以发现,我们在调用代码前后都做了一些操作,甚至可以直接拦截该方法,不让其运行。其中定义了一个 ProxyUtil 类,方便我们做一些操作:

class ProxyUtil {
    private static final String TAG = "ProxyUtil";

     start() {
        Log.d(TAG,"start: " + System.currentTimeMillis());
    }

     finish() {
        Log.d(TAG,"finish: " + log(String name) {
        Log.d(TAG,"log: " + name);
    }
}

接下去开始编写代理的实现:

class MainActivity extends AppCompatActivity {
    @Override
    protected  onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");

        创建一个与代理对象相关联的 InvocationHandler
        PersonHandler stuHandler = new PersonHandler<>(zhangsan);

        创建一个代理对象 stuProxy 来代理 zhangsan,代理对象的每个执行方法都会替换执行 Invocation 中的 invoke 方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),new Class<?>[]{Person.},stuHandler);

        代理执行 setName 的方法,代理实例内部又会调用 stuHandler 的 invoke方法,在通过反射调用了调用了被代理对象的方法
        stuProxy.setName("王五");
}

看下打印输出:

可以发现代理成功了。并且我们在调用方式的之前之后,都做了一些操作。Spring 的 AOP 其就是通过动态代理的机制实现的。

?

其中,我们将?stuProxy 的类名打印出来:

Log.d(TAG,"onCreate: " + stuProxy.getClass().getCanonicalName());

?发现其名字竟然是 $Proxy0。具体原因下面会解释。

源码分析

上面我们利用 Proxy 类的 newProxyInstance 方法创建了一个动态代理对象,查看该方法的源码:?

/**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * { IllegalArgumentException} for the same reasons that
     * { Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     *    interfaces the list of interfaces for the proxy class
     *          to implement
     *    h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to { getProxyClass}
     *          are violated
     *   SecurityException if a security manager,<em>s</em>,is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given { loader} is { null} and
     *               the caller's class loader is not { null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               { RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface,{ intf},*               the caller's class loader is not the same as or an
     *               ancestor of the class loader for { intf} and
     *               invocation of { SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to { intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               { SecurityManager#checkPermission s.checkPermission} with
     *               { ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     *   NullPointerException if the { interfaces} array
     *          argument or any of its elements are { null},or
     *          if the invocation handler,1)"> h},is
     *          { null}
     */
    @CallerSensitive
    static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
         IllegalArgumentException
    {
     // 判空,判断 h 对象是否为空,为空就抛出 NullPointerException 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); * Invoke its constructor with the designated invocation handler. */ try { ) { checkNewProxyPermission(Reflection.getCallerClass(),cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { Void run() { cons.setAccessible(true); return ; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw InternalError(e.toString(),e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { InternalError(t.toString(),t); } } (NoSuchMethodException e) {

     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    static Class<?> getProxyClass0(ClassLoader loader,1)">... interfaces) {
     // 数量超过 65535 就抛出异常,665535 这个就不用说了吧
if (interfaces.length > 65535) { 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 proxyClassCache.get(loader,interfaces); }

?最后发现会对生成的代理类进行缓存,有了,就不直接返回,没有的,还得生成代理类,我们继续往下走:

proxyClassCache = new WeakCache<>(new KeyFactory(),1)">new ProxyClassFactory());

?关键点在于??ProxyClassFactory 这个类,从名字也可以猜出来这个类的作用。看看代码:


     * A factory function that generates,defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     final  ProxyClassFactory
        implements BiFunction<ClassLoader,Class<?>[],Class<?>>
    {
         prefix for all proxy class names 定义前缀
        final String proxyClassNamePrefix = "$Proxy";

         next number to use for generation of unique proxy class names  原子操作,适用于多线程
        final AtomicLong nextUniqueNumber =  AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader,Class<?>[] interfaces) {

            Map<Class<?>,Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 
                Class<?> interfaceClass = ;
                 {
            // 通过反射获取到接口类 interfaceClass
= Class.forName(intf.getName(),1)">false,loader); } (ClassNotFoundException e) { }
         // 所得到的接口类与传进来的不相等,说明不是同一个类
if (interfaceClass != intf) { IllegalArgumentException( intf + " is not visible from class loader"); } * Verify that the Class object actually represents an * interface. */ interfaceClass.isInterface()) { IllegalArgumentException( interfaceClass.getName() + " is not an interface" * Verify that this interface is not a duplicate.
if (interfaceSet.put(interfaceClass,Boolean.TRUE) != ) { IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; * 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. */ int flags = intf.getModifiers(); Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0,n + 1)); if (proxyPkg == ) { proxyPkg = pkg; } else pkg.equals(proxyPkg)) { IllegalArgumentException( "non-public interfaces from different packages"); } } } ) { if no non-public proxy interfaces,use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } * Choose a name for the proxy class to generate. long num = nextUniqueNumber.getAndIncrement();
       // 生产代理类的名字 String proxyName
= proxyPkg + proxyClassNamePrefix + num; * Generate the specified proxy class. byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName,interfaces,accessFlags); defineClass0(loader,proxyName,proxyClassFile,0 (ClassFormatError e) { * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). IllegalArgumentException(e.toString()); } } }

这里会通过反射获取接口的各种修饰符,包名等,然后根据规则命名代理类,最后调用?ProxyGenerator.generateProxyClass 生成了代理类。

?ProxyGenerator.generateProxyClass 具体实现在 eclipse 上打开后,说是找不到源码:

?不过,从其他地方找到了部分代码:

byte[] generateProxyClass(final String name,Class[] interfaces)  
   {  
       ProxyGenerator gen =  ProxyGenerator(name,interfaces);  
     这里动态生成代理类的字节码,由于比较复杂就不进去看了  
       byte[] classFile = gen.generateClassFile();  
  
     如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上  
       if (saveGeneratedFiles) {  
           java.security.AccessController.doPrivileged(  
           new java.security.PrivilegedAction<Void>() {  
                Void run() {  
                    {  
                       FileOutputStream file =  
                           new FileOutputStream(dotToSlash(name) + ".class");  
                       file.write(classFile);  
                       file.close();  
                       ;  
                   }  (IOException e) {  
                        InternalError(  
                           "I/O exception saving generated file: " + e);  
                   }  
               }  
           });  
       }  
  
     返回代理类的字节码  
        classFile;  
   }  

我们可以自己试试 ProxyGenerator.generateProxyClass 的功能。

 ProxyGeneratorUtils {
    
     * 把代理类的字节码写到硬盘上 
     *  path 保存路径 
      writeProxyClassToHardDisk(String path) {
 获取代理类的字节码  
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11",Student..getInterfaces());
 
        FileOutputStream out = ;
 
         {
            out =  FileOutputStream(path);
            out.write(classFile);
            out.flush();
        }  (Exception e) {
            e.printStackTrace();
        } finally {
                out.close();
            }  (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

main 方法里面进行调用 :

 Main {
     main(String[] args) {
        ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy0.class");
    }
}

可以发现,在根目录下生成了一个??$Proxy0.class 文件,文件内容反编译后如下:

import java.lang.reflect.InvocationHandler;
 java.lang.reflect.Method;
 java.lang.reflect.Proxy;
 java.lang.reflect.UndeclaredThrowableException;
 proxy.Person;

class $Proxy0 extends Proxy  Person
{
   Method m1;
   Method m2;
   Method m3;
   Method m0;
  
  
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  *
  *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  *父类持有:protected InvocationHandler h;
  *Proxy构造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
   $Proxy0(InvocationHandler paramInvocationHandler)
     
  {
    (paramInvocationHandler);
  }
  
  这个静态块本来是在最后的,我把它拿到前面来,方便描述
   
  {
    
    {
      看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals",1)">new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString",1)">new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveMoney",1)">]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode",1)">]);
      ;
    }
     (NoSuchMethodException localNoSuchMethodException)
    {
       NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
     (ClassNotFoundException localClassNotFoundException)
    {
       NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
 
  
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this,m3,null);这里简单,明了。
  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
   giveMoney()
    this.h.invoke(this,1)">);
      catch (Error|RuntimeException localError)
    {
       localError;
    }
     (Throwable localThrowable)
    {
       UndeclaredThrowableException(localThrowable);
    }
  }

  注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。

}

jdk 为我们的生成了一个叫 $Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。

我们可以对 InvocationHandler 看做一个中介类,中介类持有一个被代理对象,在 invoke 方法中调用了被代理对象的相应方法,而生成的代理类中持有中介类,因此,当我们在调用代理类的时候,就是再调用中介类的 invoke 方法,通过反射转为对被代理对象的调用。

代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的 invoke 方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。

生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了 Proxy 类,所以也就决定了 java 动态代理只能对接口进行代理,Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理。

?

参考文章:

1、java的动态代理机制详解

2、彻底理解JAVA动态代理

3、java 1.8 动态代理源码分析

4、java动态代理实现与原理详细分析

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读