java 基础 --- 动态代理和静态代理

Posted float123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 基础 --- 动态代理和静态代理相关的知识,希望对你有一定的参考价值。

问题  :

  • 代理的应用场景是什么
  • 动态代理的底层原理是什么,为什么只能继承接口

概述

         代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法。这种模式在AOP (切面编程)中非常常见,使用的场景比如事务,网络代理等。他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。本文将会介绍代理的两种方式 : 静态代理和动态代理。

  • 静态代理 :由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
  • 动态代理 : 代理类在程序运行时创建的代理方式被成为动态代理。

 

代码解析

静态代理

           静态代理例子

public interface ISProxyInterface {
   void doSomething();
}


public class STargetObject implements ISProxyInterface{
    public void doSomething(){
        System.out.println("静态代理真实类:do some thing");
    }

}


/**
 *  静态代理类
 */
public class StaticProxy implements  ISProxyInterface {
    private STargetObject target;

    public StaticProxy(STargetObject target) {
        this.target = target;
    }

    public void doSomething(){
        System.out.println("调用之前");
        target.doSomething();
        System.out.println("调用之后");

    }
}





public class ProxyMain {
    public static void main(String[] args) {
        //静态代理测试
        STargetObject target = new STargetObject();
        StaticProxy proxy = new StaticProxy(target);
        proxy.doSomething();

        System.out.println("------");

        //动态代理测试
        TargetInterface Dtarget = new DTargetObject();
        TargetHandler handler = new TargetHandler(Dtarget);
        TargetInterface Dproxy = (TargetInterface) Proxy.newProxyInstance(Dtarget.getClass().getClassLoader(), Dtarget.getClass().getInterfaces(), handler);
        Dproxy.doSomeThing();
    }
}

        可以看到,静态代理和真实类都拥有同样的接口,这让它看起来似乎和真实类一样,当调用这个接口时,实际走的是真实类的方法。

动态代理

         动态代理例子。

 

public interface TargetInterface {
    void doSomeThing();
}


/**
*  动态代理处理类
*/
public class TargetHandler implements InvocationHandler {

    private Object mObject;

    public TargetHandler(Object object) {
        mObject = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用之前");
        Object result = method.invoke(mObject, args);
        System.out.println("调用之后");
        return result;
    }
}


public class DTargetObject implements TargetInterface {

    @Override
    public void doSomeThing() {
        System.out.println("动态代理类 : do some thing !");
    }
}

动态代理原理解析

      我们看到在ProxyMain 中调用了Proxy.newProxyInstance这个方法,生成了一个代理,而这个代理类中正是我们需要的。要想知道这中间发生了什么,就要看看 Proxy.newProxyInstance 做了什么事。

      看这个方法的实现前我们先看一下,这个方法的注解 :

Throws:
	IllegalArgumentException 
	    - if any of the restrictions on the parameters that may be passed to getProxyClass  are violated

	SecurityException - if a security manager, s, is present and any of the following conditions is met:
		- the given loader is null and the caller\'s class loader is not null and the invocation of s.checkPermission with RuntimePermission("getClassLoader") permission denies access;
		- 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 s.checkPackageAccess() denies access to intf;
		- any of the given proxy interfaces is non-public and the caller class is not in the same runtime package as the non-public interface and the invocation of s.checkPermission with ReflectPermission("newProxyInPackage.{package name}") permission denies access.

    NullPointerException 
    	- if the interfaces array argument or any of its elements are null, or if the invocation handler, h, is null

 

    @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);  //封装或是生产一个代理类

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {                       
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});   //产生了一个实例返回
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

     

       我们最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的。我们可以通过打印出来这个临时的类是如何的来了解动态代理的原理。在ProxyMain 中添加如下代码 :

        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", DTargetObject.class.getInterfaces());
        String path = "E:/DTargetObject.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }

        打开路径下就会得到一个class 的文件,我们再用 java decompiler(反编译工具)来查看class 文件。

import Proxy.Dynatic.TargetInterface;
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 TargetInterface
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);             //构造方法,InvocationHandler 和我们的代理处理类是不是很像,就是它!
    //而我们的InvocationHandler 又持有一个真实的对象,所以我们这里可以预测下面会调用 invoke方法
  }
  
  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void doSomeThing()     //看到了我们熟悉的方法 
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);    //刚刚从构造函数传过去的InvocationHandler
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  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("Proxy.Dynatic.TargetInterface").getMethod("doSomeThing", new Class[0]);   //这是我们想调用的方法
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

        从上面的源码我们可以知道 :

  • JDK 动态代理的原理底层是反射.
  • 由于 $Proxy 已经继承了 Proxy ,(java单继承)所以我们只能是代理接口

 

参考资料

以上是关于java 基础 --- 动态代理和静态代理的主要内容,如果未能解决你的问题,请参考以下文章

Java基础加强——动态代理

java 基础 --- 动态代理和静态代理

java中静态代理和动态代理

Java基础-静态代理以及动态代理

Java动态代理

Java动态代理