Java代理机制分析——JDK代理(Proxy、InvocationHandler与示例)

前言

  • Java的代理机制使得咱们能够在不修改原始的Java对象的代码的状况下,对原始的Java对象进行功能的加强。
  • Java的代理机制包括了静态代理和动态代理,静态代理须要咱们为每一个须要被代理的对象编写对应的代理类,当被代理对象较多时,使用静态代理就不太合适了,而动态代理使得咱们能够把须要被代理的对象做为参数,传递给用于生成代理对象的方法,并在运行时动态地建立代理对象。
  • 动态代理又包括了使用JDK api实现的JDK代理和使用cglib包实现的cglib代理,JDK代理须要被代理对象实现了接口,经过invocationHandler调用原对象方法实现代理,而cglib不须要被代理对象实现接口,经过继承原对象并重写字节码的方式实现代理。
  • 本文介绍了JDK代理的机制以及简单的使用过程,JDK代理的核心包括Proxy类和InvocationHandler接口。
  • 如图为Proxy类的UML图。
    java.lang.reflect.Proxy UML图

1 核心——Proxy类

1.1 变量/域

1.1.1 proxyClassCache

  • 类型:WeakCache<ClassLoader, Class<?>[], Class<?>>
  • 目的:缓存生成的代理类的class对象的工厂对象,并经过缓存的工厂对象产生代理类的class对象。
  • 原理:在Proxy调用newProxyInstance方法时,会首先在缓存中查找是否存在对应代理类的class对象,若找到了则能够直接利用该class对象,生成并返回新的代理对象;不然会利用ProxyClassFactory建立新的代理类的class对象(并放入缓存),生成并返回新的代理对象。proxyClassCache经过一个静态的WeakCache缓存实现,WeakCache经过嵌套的concurrentMap实现二级缓存:一级缓存的key(key)是使用传入的ClassLoader包装而成的CacheKey(继承于WeakReference,见下);二级缓存的key(subkey)是使用传入的接口class对象数组经过KeyFactory(见1.3.1)生成的,而二级缓存(valuesMap)的value是一个实现了supplier接口(见下)的对象。该对象是一个用于产生目标代理类class对象的工厂,对该对象调用get方法来获取真正的代理类的class对象。此外,在WeakCache中使用到了弱引用类WeakReference和一个弱引用队列ReferenceQueue解决concurrentMap的垃圾回收问题。
  • supplier接口:泛型函数式接口,经过重写get方法,能够返回一个对象。
  • 为何要使用WeakReference和ReferenceQueue:要解决这个问题,首先须要知道什么是强引用和弱引用。以本例来讲,当咱们直接向concurrentMap中存放一个键值对时,咱们向map中添加了三个对象的引用,键值对的entry(node),key和value,引用默认是强引用,意味着当咱们没有显式地从map中remove(key)时,这三个对象是不会被垃圾回收的。所以若是程序长时间运行而没有显式remove,map中加入了愈来愈多的对象,就可能形成性能问题。与强引用不一样的是,若是一个对象只存在弱引用,那么它在下一次垃圾回收时,就会被回收。所以,在这里使用了WeakReference弱引用包装类和ReferenceQueue弱引用队列来解决对象回收的问题。具体的作法是,将map的key包装到WeakReference中,实现key到其余对象的弱引用,可是若是只有key使用了弱引用是不够的,由于就算key被垃圾回收了,entry和value仍然没有被回收。利用ReferenceQueue能够解决这个问题,WeakReference在建立的时候须要两个参数,一个是被包装的key,另外一个则是一个指定的ReferenceQueue,key被注册到该ReferenceQueue中。当key被回收时,会向该ReferenceQueue中加入这个key。这时会产生一个看似矛盾的问题,为何想要key被回收,可是又要在它被回收的时候加入队列中?这是由于在key被回收时,咱们还须要经过它去清除map中的整个entry对象。在本例中,concurrentMap每次调用get方法时,都会执行一次清除工做,去清除存在于自身中且与ReferenceQueue中的key对应的entry,key和value,这样就能够及时清理掉一级缓存concurrentMap中过时的对象。

1.2 方法

1.2.1 newProxyInstance(核心)

  • 目的:获取新的代理对象。
  • 原理:根据传入的类加载器、接口class对象的数组和invocationHandler实现类,从proxyClassCache中获取一个实现了传入的接口方法的代理类的class对象,随后经过反射的方式获取class对象中的constructor,调用newInstance方法生成代理对象实例并返回。

1.2.2 getProxyClass

  • 目的:从proxyClassCache中根据类加载器和接口class对象的数组返回代理类的class对象(比newProxyInstance少一个经过反射生成代理对象的步骤)。

1.2.3 defineClass0(核心)

  • 目的:生成新的代理类的class对象。
  • 原理:defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length),经过方法生成的新的代理类的class对象中,成员变量/实例域中包含了指定的接口中的全部方法的method对象和后期经过构造方法传入的invocationHandler实现类,成员方法中实现了指定的接口中的全部方法。在外部调用代理对象的方法时,这些方法内部其实是经过代理对象中的invocationHandler,转而调用invoke(Object proxy, Method method, Object[] args),其中proxy表明代理对象自身this,method是代理对象方法对应的成员变量method对象,args是method所需参数。

1.3 内部类

1.3.1 KeyFactory

  • 目的:生成proxyClassCache中二级缓存(valuesMap)的键,该键标识了传入的一组class对象。
  • 原理:KeyFactory是BiFunction接口的实现类(泛型函数式接口,经过重写apply方法,能够传入两个对象,运算而后返回一个结果对象),泛型参数为BiFunction<ClassLoader, Class<?>[], Object>,表示能够传入一个类加载器和一个接口class对象的数组,经过apply方法**生成一个Key?对象**(见1.3.3),并返回。KeyFactory实际上并无调用classLoader,仅仅经过class<?>[]的长度判断返回哪种Key。

1.3.2 ProxyClassFactory

  • 目的:生成新的代理类的class对象。
  • 原理:ProxyClassFactory是BiFunction接口的实现类,泛型参数为BiFunction<ClassLoader, Class<?>[], Class<?>>,表示能够传入一个类加载器和一个接口class对象的数组,经过apply方法生成一个代理类的class对象,并返回。apply方法中包括了生成代理类class对象的一些预处理,如检查传入的class对象数组中的class对象是否被类加载器可见、是不是接口、生成新的代理类的类名proxyName等,最后调用native的defineClass0方法(见)生成新的代理类的class对象,该class对象随后用于生成代理类实例。ProxyClassFactory的apply方法在对proxyClassCache的二级缓存的value——工厂对象调用get方法时调用。

1.3.3 Key1,Key2,KeyX

  • 目的:做为proxyClassCache二级缓存的键。
  • 原理:KeyFactory在生成Key?对象时,这个对象的实际类型由传入的接口class对象的数组大小决定,数组中大于2个class对象时生成KeyX类的对象/实例,而只有1个(最多见)或2个class对象时分别生成Key1类和Key2类的对象,这样作是由于针对后两种的状况进行了代码的优化。键与键之间也是用hashcode来比较的。

2 核心——InvocationHandler接口

2.1 目的

实现了让代理对象在调用接口方法时,转而执行加强的、被代理过的方法——invoke(Object proxy, Method method, Object[] args)。html

2.1 原理

该接口的实现类至关于一个方法调用处理器,在Proxy.newProxyInstance调用时被传入到Proxy对象中。随后,在Proxy产生了代理类的class对象,并经过反射生成代理对象时,被传入到代理对象中。所以,代理对象调用任何方法时,都会使用invocationHandler执行定义的invoke方法。java

3 示例

3.1 InvocationHandler实现类

class InvocationHandlerImpl implements InvocationHandler { 
 
  
    Object targetObject;

    public InvocationHandlerImpl(Object targetObject) { 
 
  
        this.targetObject = targetObject;
    }
	
	// 由3.4 可知,proxy表明代理对象自己(此处没有使用),method表明被代理接口中当前被调用的method对象,args为method对象参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
 
  
        // your code before method invoked
        System.out.println("start to do something..");
        Object result = method.invoke(targetObject, args);
        // your code after method invoked
        System.out.println("end to do something..");
        return result;
    }
}

3.2 被代理的接口

interface Person { 
 
  
    void eat();

    void drink();
}

3.3 main方法中建立接口的代理对象

public static void main(String[] args) { 
 
  
		// JDK代理只能代理实现了接口的对象,建立这个对象传入invocationHandler实现类中,后者调用invoke方法时,method的反射调用会使用到该对象
        Person person = new Person() { 
 
  
            @Override
            public void eat() { 
 
  
                System.out.println("eating...");
            }

            @Override
            public void drink() { 
 
  
                System.out.println("drinking...");
            }
        };
        Person proxyPerson = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{ 
 
  Person.class}, new InvocationHandlerImpl(person));
        proxyPerson.eat();
        // 打印 start to do something..
		// eating...
  		// end to do something..
    }

3.4 Proxy中自动建立的代理类class对象(简要)

public class $Proxy0 { 
 
  
    InvocationHandler invocationHandler;
    Method originalEat = xxx; // 此处为接口原方法的method对象,用于invocationHandler实现类的invoke方法经过反射来调用!
    Method originalDrink = xxx;
	
	public $Proxy0(InvocationHandler invocationHandler){ 
 
  
		this.invocationHandler = invocationHandler;
	}
	
	// 实现了接口方法,并在每一个方法中经过invocationHandler调用加强的方法
    public void eat() throws Throwable { 
 
  
        invocationHandler.invoke(this, originalEat, null);
    }

    public void drink() throws Throwable { 
 
  
        invocationHandler.invoke(this, originalDrink, null);
    }
}

参考

  • https://blog.csdn.net/jiankunking/article/details/52143504
  • https://www.cnblogs.com/liuyun1995/p/8144676.html
  • https://zhuanlan.zhihu.com/p/88759564
  • https://segmentfault.com/a/1190000011291179