博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解 Java 动态代理机制
阅读量:6290 次
发布时间:2019-06-22

本文共 17873 字,大约阅读时间需要 59 分钟。

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 接口,每一个学生都有一个自己的名字:

public class Student implements Person {        private String mName;    public Student(String name) {        mName = name;    }    public void setName(String name) {        mName = name;    }}

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

public class PersonHandler
implements InvocationHandler { // 代理的目标对象 private T mTarget; public 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 类,方便我们做一些操作:

public class ProxyUtil {    private static final String TAG = "ProxyUtil";    public static void start() {        Log.d(TAG, "start: " + System.currentTimeMillis());    }    public static void finish() {        Log.d(TAG, "finish: " + System.currentTimeMillis());    }    public static void log(String name) {        Log.d(TAG, "log: " + name);    }}

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

public class MainActivity extends AppCompatActivity {    @Override    protected void 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.class}, stuHandler); //代理执行 setName 的方法 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.     *     * 

{

@code Proxy.newProxyInstance} throws * {
@code IllegalArgumentException} for the same reasons that * {
@code Proxy.getProxyClass} does. * * @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class * to implement * @param 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 {
@code getProxyClass} * are violated * @throws SecurityException if a security manager, s, is present * and any of the following conditions is met: *

    *
  • the given {
    @code loader} is {
    @code null} and * the caller's class loader is not {
    @code null} and the * invocation of {
    @link SecurityManager#checkPermission * s.checkPermission} with * {
    @code RuntimePermission("getClassLoader")} permission * denies access;
  • *
  • for each proxy interface, {
    @code intf}, * the caller's class loader is not the same as or an * ancestor of the class loader for {
    @code intf} and * invocation of {
    @link SecurityManager#checkPackageAccess * s.checkPackageAccess()} denies access to {
    @code intf};
  • *
  • 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 * {
    @link SecurityManager#checkPermission s.checkPermission} with * {
    @code ReflectPermission("newProxyInPackage.{package name}")} * permission denies access.
  • *
* @throws NullPointerException if the {
@code interfaces} array * argument or any of its elements are {
@code null}, or * if the invocation handler, {
@code h}, is * {
@code null} */ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) throws 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 { 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
() { 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); } }

 在生成代理类的过程中,会进行一些列检查,比如访问权限之类的。接下去我们来看 getProxyClass0 方法的源码:

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

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

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

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

/**     * A factory function that generates, defines and returns the proxy class given     * the ClassLoader and array of interfaces.     */    private static final class ProxyClassFactory        implements BiFunction
[], Class
> { // prefix for all proxy class names 定义前缀 private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names 原子操作,适用于多线程 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class
apply(ClassLoader loader, Class
[] interfaces) { Map
, 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 = null; try {
            // 通过反射获取到接口类 interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { }          // 所得到的接口类与传进来的不相等,说明不是同一个类 if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " 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.put(interfaceClass, Boolean.TRUE) != null) { throw new 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. */ for (Class
intf : interfaces) { int flags = intf.getModifiers(); if (!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 == 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 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); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (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). */ throw new IllegalArgumentException(e.toString()); } } }

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

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

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

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

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

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

main 方法里面进行调用 :

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

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

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import proxy.Person;public final class $Proxy0 extends Proxy implements Person{  private static Method m1;  private static Method m2;  private static Method m3;  private static Method m0;    /**  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。  *  *super(paramInvocationHandler),是调用父类Proxy的构造方法。  *父类持有:protected InvocationHandler h;  *Proxy构造方法:  *    protected Proxy(InvocationHandler h) {  *         Objects.requireNonNull(h);  *         this.h = h;  *     }  *  */  public $Proxy0(InvocationHandler paramInvocationHandler)    throws   {    super(paramInvocationHandler);  }    //这个静态块本来是在最后的,我把它拿到前面来,方便描述   static  {    try    {      //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管      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.Person").getMethod("giveMoney", 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());    }  }   /**  *   *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。  *this.h.invoke(this, m3, null);这里简单,明了。  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。  */  public final void giveMoney()    throws   {    try    {      this.h.invoke(this, m3, null);      return;    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。}

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

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

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

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

 

参考文章:

1、

2、

3、

4、

转载地址:http://wnkta.baihongyu.com/

你可能感兴趣的文章
资源文件夹res/raw和assets的使用
查看>>
UINode扩展
查看>>
LINUX常用命令
查看>>
百度云盘demo
查看>>
概率论与数理统计习题
查看>>
初学structs2,简单配置
查看>>
Laravel5.0学习--01 入门
查看>>
时间戳解读
查看>>
sbin/hadoop-daemon.sh: line 165: /tmp/hadoop-hxsyl-journalnode.pid: Permission denied
查看>>
@RequestMapping 用法详解之地址映射
查看>>
254页PPT!这是一份写给NLP研究者的编程指南
查看>>
《Data Warehouse in Action》
查看>>
String 源码浅析(一)
查看>>
Spring Boot 最佳实践(三)模板引擎FreeMarker集成
查看>>
Fescar 发布 0.2.3 版本,支持 Redis 和 Apollo
查看>>
Google MapReduce到底解决什么问题?
查看>>
CCNP-6 OSPF试验2(BSCI)
查看>>
Excel 2013 全新的图表体验
查看>>
openstack 制作大于2TB根分区自动扩容的CENTOS镜像
查看>>
Unbuntu安装遭遇 vmware上的Easy install模式
查看>>