江苏 无锡 缪小东

写到代理模式这章,不得不提到JDK中的动态代理,它是java语言自身对动态代理的支持,类似于JDK中在java.util包中提供Observable类和Observer接口提供对观察者模式的语言级支持。关于动态代理的好处可以从网络上流行的<<JAVA中用动态代理类实现记忆功能>>、<<使用JAVA中的动态代理实现数据库连接池>>、<<通过JAVA的动态代理机制控制事务>> 、<<用Java动态代理实现AOP>>……看出动态代理的优点――动态地为软件增加功能。应用的文章很多,出于自身的好奇我们来研究JDK中动态代理的实现吧!这就是本博客所谓的“任何东西只有精通了,才能更好地使用”,有别于所谓的“很好地使用它,然后再去精通它”。下面我们开始对java语言中动态代理的源代码的研究吧!

一、动态代理的相关类

JDK中和动态代理直接相关的主要有InvocationHandler接口和Proxy类。InvocationHandler接口相当于Proxy类的CallBack
Interface。

下图为InvocationHandler接口的类图,该接口中仅定义了Object:invoke(Object obj,Method method, Object[] args)一个方法。第一个参数proxy一般是指具体的被代理类,即代理模式中的目标对象;method是被代理的方法,args为该方法的参数数组。该接口在动态代理中由客户实现。

三、Proxy类

Proxy为动态代理类,是动态代理的核心类,其作用类似于代理模式中的代理。从上面的类图可以看出Proxy类中包含的全是静态的方法和成员变量,这是一个纯粹的工具类,因此其源代码中含有一个私有的构造器,在某种意义上可以看作为一个单例模式的特殊情形。(原因为:1.不能实例化;2.提供统一的入口)。这个类中包含很多很有意思的实现,下面我们还是看看其源代码吧!

package java.lang.reflect;

import java.lang.ref.*;

import java.util.*;

import sun.misc.ProxyGenerator;           //sun将产生代理对象的类,放在sun.misc包中

public class Proxy implements java.io.Serializable {                    //实现了Serializable接口,因此可以保存到流中

private static final long serialVersionUID = -2222568056686623797L;   //序列化时使用的版本号

//以下的变量用于代理对象创建时,名称各部分的组合

//以下几个成员变量组合在一起,形成被创建的代理对象的名称

//这些被ProxyGenerator创建的代理对象,以此名字,存入当前Proxy对象内部的cache中

//被创建代理对象的名称包含3部分:

//                          1.包名proxyPkg ;2.代理对象的名称前缀;3.代理被创建的数目num;

//                          该数目表示当前被创建的代理对象是使用ProxyGenerator创建的第n个代理对象

//下面是代理对象的名称前缀

private final static String proxyClassNamePrefix = "$Proxy";

//初始化时,代理被创建的个数为0

private static long nextUniqueNumber = 0;

//由于可能有多个客户同时使用ProxyGenerator创建代理对象,因此必须进行同步

//                                   此同步过程使代理被创建的数目唯一

private static Object nextUniqueNumberLock = new Object();

//表示某个代理对象正在被创建

private static Object pendingGenerationMarker = new Object();

//代理对象实例化时需要的的构造参数

private final static Class[] constructorParams ={ InvocationHandler.class };

//类转载器的cache

private static Map loaderToCache = new WeakHashMap();

//所有已经被创建的代理对象的集合,每次要创建新的代理对象,都会先到该集合查找是否存在

//                 这其实是一个代理对象的缓存

private static Map proxyClasses =Collections.synchronizedMap(new WeakHashMap());

protected InvocationHandler h;                          //唯一的非静态的成员变量,主要用于protected构造器

protected Proxy(InvocationHandler h) {   //protected构造器,给继承提供了可能

this.h = h;

}

private Proxy() {  }                       //工具类,不能实例化,因此为私有构造器

//使用特定的类加载器,加载某个类,从而得到此代理对象的Class

public static Class<?> getProxyClass(ClassLoader loader,  Class<?>... interfaces)throws IllegalArgumentException{

if (interfaces.length > 65535) {                  //构造器太多,抛出异常

throw new IllegalArgumentException("interface limit exceeded");

}

Class proxyClass = null;                //初始化一个Class

//将interface的名字 collect interface
names to use as key for proxy class cache */

String[] interfaceNames = new String[interfaces.length];

Set interfaceSet = new HashSet();          //避免重复

for (int i = 0; i < interfaces.length; i++) {

String interfaceName = interfaces[i].getName();                  //得到名称

Class interfaceClass = null;

try {

interfaceClass = Class.forName(interfaceName, false, loader); //使用类装载器转载类

} catch (ClassNotFoundException e) {   }

if (interfaceClass != interfaces[i]) {            //两者不等抛出异常

throw new IllegalArgumentException(interfaces[i] + " is not visible from class loader");

}

if (!interfaceClass.isInterface()) {                        //不是接口抛出异常

throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface");

}

if (interfaceSet.contains(interfaceClass)) {         //已经存在抛出异常

throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());

}

interfaceSet.add(interfaceClass);                       //不存在则放入hashset中

interfaceNames[i] = interfaceName;                  //置换输入中的构造器

}

Object key = Arrays.asList(interfaceNames);   //将上面的数组转化为List对象

//以上完成对构造器的处理

Map cache;

synchronized (loaderToCache) {                               //同步化类装载器的Cache

cache = (Map) loaderToCache.get(loader);

if (cache == null) {

cache = new HashMap();                       //该Cache以类装载器为key,value也为一个Cache

loaderToCache.put(loader, cache);

}

}

synchronized (cache) {

do {

Object value = cache.get(key);                         //该Cache以Class数组为key以proxyClass为value

if (value instanceof Reference) {

proxyClass = (Class) ((Reference) value).get();   //保存在Cache中的value为Reference

}

if (proxyClass != null) {                                               //代理对象存在

return proxyClass;                                             //返回此proxyClass

} else if (value == pendingGenerationMarker) {

try {

cache.wait();                                   //其它线程正在创建代理对象,则本线程等待

} catch (InterruptedException e) {   }

continue;

} else {

cache.put(key, pendingGenerationMarker);

break;

}

} while (true);

}

try {

String proxyPkg = null;                                               // 代理对象所在的包

for (int i = 0; i < interfaces.length; i++) {             //遍历Class数组

int flags = interfaces[i].getModifiers();                //得到Class的修饰符

if (!Modifier.isPublic(flags)) {                     //public的修饰符

String >

int n = name.lastIndexOf('.');

String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  //package名称

if (proxyPkg == null) {

proxyPkg = pkg;                             //

} else if (!pkg.equals(proxyPkg)) {

throw new IllegalArgumentException( "non-public interfaces from different packages");

}

}

}//for

if (proxyPkg == null) {

proxyPkg = "";             // 默认包名

}

{

long num;

synchronized (nextUniqueNumberLock) {                          //同步块

num = nextUniqueNumber++;                                   //每次递增1

}

String proxyName = proxyPkg + proxyClassNamePrefix + num;  //名称为三者的组合

//以下是创建ProxyClass的过程

byte[] proxyClassFile =ProxyGenerator.generateProxyClass(proxyName, interfaces);

//上面是使用ProxyGenerator将指定名称代理类的.class文件转化为byte数组

//在类加载器中进一步执行此.class文件,在java语言中经常称java为解释型语言

//字节码就是被解释的中间代码,它是独立于平台的,其执行是在虚拟机上

//                                   有兴趣的朋友多研究研究编译原理、以及虚拟机的实现

//                                   sun已经将其Compiler和VM的源代码开源了,有能力可以研究研究

/ /利用刚才产生的byte数组,使用类加载器产生此ProxyClass

try {

使用类装载器,装载指定名称的代理类

proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);

} catch (ClassFormatError e) {

throw new IllegalArgumentException(e.toString());

}

}

// 将新创建的ProxyClass放入Chche中

proxyClasses.put(proxyClass, null);

} finally {

synchronized (cache) {

if (proxyClass != null) {

cache.put(key, new WeakReference(proxyClass));           //将此Renference放入cache中

} else {

cache.remove(key);

}

cache.notifyAll();                    //多线程的wait/notify机制

}

}

return proxyClass;

}

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{

if (h == null) {    throw new NullPointerException();                  }        //InvocationHandler为null抛出异常

Class cl = getProxyClass(loader, interfaces);                               //使用上面的方法得到一个ProxyClass

try {

Constructor cons = cl.getConstructor(constructorParams);         //得到此类的构造器

return (Object) cons.newInstance(new Object[] { h });                  //使用Handler构造对象

} catch (NoSuchMethodException e) {

throw new InternalError(e.toString());

} catch (IllegalAccessException e) {

throw new InternalError(e.toString());

} catch (InstantiationException e) {

throw new InternalError(e.toString());

} catch (InvocationTargetException e) {

throw new InternalError(e.toString());

}

}

public static boolean isProxyClass(Class<?> cl) {                                          //判断某个Class是否为ProxyClass

if (cl == null) {    throw new NullPointerException();      }                 //输入为null抛出异常

return proxyClasses.containsKey(cl);                                          //返回该Proxy的Chche中是否存在该Class

}

//得到某个ProxyClass对象对应的InvocationHandler

public static InvocationHandler getInvocationHandler(Object proxy)throws IllegalArgumentException{

if (!isProxyClass(proxy.getClass())) {               //该对象不是ProxyClass则抛出异常

throw new IllegalArgumentException("not a proxy instance");

}

Proxy p = (Proxy) proxy;                                 //是ProxyClass则downcast

return p.h;                                                          //返回此ProxyClass的成员变量

}

//到目前为止,我们可以猜测在创建ProxyClass时,

//肯定使用该Proxy类的子类或者其protected的构造器中的一种

private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);

//使用JNI创建一个Class

}

上面使用多个Cache,以下是这些Cache的层次图:

四、使用动态代理的例子

下面给出一个具体的例子:

//ServiceIF.java

public interface ServiceIF {

public void doService();

}

以上是具体提供服务的对象的接口。下面给出其实现。

//Service.java

public class Service implements ServiceIF{

public void doService(){

System.out.println("正在执行您申请的服务!");

}

}

使用JDK中的动态代理时必须实现InvocationHandler接口,实现该接口的对象一般会封装代理模式中的目标对象,同时实现其中的invoke方法。以下是实现该接口的代码:

//DynamicProxyInvocationHandler.java

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class DynamicProxyInvocationHandler implements InvocationHandler {

private Object realService = null;

public DynamicProxyInvocationHandler(Object service) {

this.realService = service;

}

public Object invoke(Object proxy, Method m, Object[] args){

Object result = null;

System.out.println("您正在申请服务" + m.getName());

try {

result = m.invoke(realService , args);

}catch(Exception ex) {

System.exit(1);

}

System.out.println("您申请的服务 " + m.getName() + " 已经完成 ");

return result;

}

}

接口中的invoke方法包含三个参数:Object、Method、Object[],同时包含一个Object类型的返回值。该方法的含义为:使用Object[]类型的参数列表args调用proxy对象的m方法,返回Object类型的对象。 在上面的实现中,在调用具体对象的方法前首先打印正在申请某个对象的某个方法,接着调用某个对象的某个方法,在调用具体对象的方法后打印申请的服务已经完成。这就是我们上面所说的为真实对象增加功能。下面是一段最后的测试代码:

//DynamicProxyTest.java

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class DynamicProxyTest {

public static void main(String args[]) {

Service realService = new Service();

ServiceIF proxy = (ServiceIF)Proxy.newProxyInstance(

realService.getClass().getClassLoader(),

realService.getClass().getInterfaces(),

new DynamicProxyInvocationHandler(realService));

proxy.doService();

}

}

在测试中首先创建一个真实的服务对象,即代理模式中的目标对象,然后使用Proxy类的newProxyInstance静态方法,创建一个具体的代理对象,由于代理对象和被代理对象具有相同的接口ServiceIF,因此我们在该代理对象被创建后downcast为ServiceIF接口,接着调用该代理对象的服务方法。下图是执行结果图:

有兴趣可以继续研究编译器的实现、字节码的细节、以及虚拟机的实现。

JDK中的动态代理的更多相关文章

  1. 动态代理(一)——JDK中的动态代理

    在开始动态代理的描述之前,让我们认识下代理.代理:即代替担任执行职务.在面向对象世界中,即寻找另一个对象代理目标对象与调用者交互.Java中分为静态代理和动态代理.这里对于静态代理不做详述.它们之间的 ...

  2. 通过模拟JDK中的动态代理,由浅入深讲解动态代理思想.

    目录 场景引入 动态代理引入 动态代理进阶 总结 个人认为动态代理在设计模式中算是比较难的, 本篇文章将从无到有, 从一个简单代码示例开始迭代, 逐步深入讲解动态代理思想. 场景引入 假设现在有一个坦 ...

  3. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...

  4. Spring框架中的JDK与CGLib动态代理

    JDK和CGLib动态代理区别 JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler ...

  5. spring框架中JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/ ...

  6. Spring AOP中的动态代理

    0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  Spring AOP中的动态代理机制 2.1  ...

  7. 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)

    Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC   目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...

  8. 【java高级编程】JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...

  9. JDK、CGlib动态代理详解

    Java动态代理之JDK实现和CGlib实现(简单易懂)      一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...

  10. JDK和CGLIB动态代理区别

    背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...

随机推荐

  1. OData – 坑

    前言 OData 有很多很多的坑,我的主张是能少用一样是一样,比如 Batch Processing 不要用,Inheritance 不要用,除了 GET 其它 PUT POST DELETE 都不要 ...

  2. 1Panel:一个现代化、开源的 Linux 服务器运维管理面板

    前言 之前有小伙伴问:Linux 服务器运维管理除了宝塔,还有其他值得推荐的管理软件吗?,今天大姚给大家分享一个现代化.开源的 Linux 服务器运维管理面板:1Panel. 项目介绍 1Panel是 ...

  3. 系统编程-文件IO-fcntl系统调用

    原型: #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); 功 ...

  4. 基于RHEL 9 搭建 KVM 虚拟化环境

    一.准备工作 1. 检查硬件虚拟化支持 KVM 要求处理器支持硬件虚拟化技术:Intel VT-x(虚拟化技术扩展)或 AMD-V(虚拟化技术扩展). 检查方法: 使用以下命令检查 CPU 是否支持虚 ...

  5. NL2SQL之DB-GPT-Hub<详解篇>:text2sql任务的微调框架和基准对比

    NL2SQL之DB-GPT-Hub<详解篇>:text2sql任务的微调框架和基准对比 随着生成式人工智能(Artificial Intelligence Generated Conten ...

  6. emmc寿命

    EMMC器件寿命 1)先确认EMMC器件NAND FLASH类型,是MLC还是TLC,一般是TLC,器件手册标称1000-3000次,取平均值2000次作为评估: 2)在OS下查看EMMC器件当前使用 ...

  7. manim边学边做--无向图

    无向图属于数学中的图论这一学科, 所谓无向图G,就是由顶点集V(非空集合)和边集E(由V中元素构成的无序二元组的集合)组成的图, 可表示为G=(V,E). 在无向图中,边没有方向,即从顶点A到顶点B的 ...

  8. kotlin类与对象——>扩展

    1,扩展概念(OC中早期就有此功能) Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式.这通过叫做 扩展 的特殊声明完成.例如,你可以为一个你不能修改的.来自第三方库中 ...

  9. SQLSEVER 实现货币数字转中文汉字

    SQLSEVER 实现数字转换成中文(货币) -- ============================================= -- Author: LearnerPing -- Cr ...

  10. 快速部署mysql并开启binlog

    curl -fsSL https://get.docker.com | bash yum -y install docker-ce sudo systemctl start docker sudo s ...