转载自 http://blog.csdn.net/scplove/article/details/52451899

代理:设计模式

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

java动态代理的类和接口(jdk1.6源码)

1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
public static boolean isProxyClass(Class<?> cl) // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2,java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。

/**该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
*/
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3,java.lang.ClassLoader:类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。 
每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数

动态代理机制

java动态代理创建对象的过程为如下步骤: 
1,通过实现 InvocationHandler 接口创建自己的调用处理器;

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..); 
  • 1
  • 2
  • 3

2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 
  • 1
  • 2

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
  • 1
  • 2

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
  • 1
  • 2

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

动态代理的注意点: 
1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包,

2,生成的代理类为public final,不能被继承,

3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。

4,类继承关系:

 
Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。

5,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString, 
代码在反编译中 

一个动态代理的demo

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;  public class HelloServiceProxy implements InvocationHandler {  private Object target;    /**  * 绑定委托对象并返回一个【代理占位】 * @param target 真实对象 * @return  代理对象【占位】 */    public  Object bind(Object target, Class[] interfaces) {    this.target = target;    //取得代理对象    return Proxy.newProxyInstance(target.getClass().getClassLoader(),    target.getClass().getInterfaces(), this);}    @Override    /** * 同过代理对象调用方法首先进入这个方法. * @param proxy --代理对象 * @param method -- 方法,被调用方法. * @param args -- 方法的参数 */  public Object invoke(Object proxy , Method method, Object[] args) throws Throwable {    System.err.println("############我是JDK动态代理################");  Object result = null;    //反射方法前调用  System.err.println("我准备说hello。");    //反射执行方法  相当于调用target.sayHelllo;  result=method.invoke(target, args);  //反射方法后调用.  System.err.println("我说过hello了");    return result;    }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

其中,bind方法中的newProxyInstanc方法,就是生成一个代理对象,第一个参数是类加载器,第二个参数是真实委托对象所实现的的接口(代理对象挂在那个接口下),第三个参数this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy作为对象的代理。

invoke方法有三个参数:第一个proxy是代理对象,第二个是当前调用那个方法,第三个是方法的参数。

public class ProxyTest {  public static void main(String[] args) {   HelloServiceProxy proxy = new HelloServiceProxy();  HelloService service = new HelloServiceImpl();  //绑定代理对象。  service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});  //这里service经过绑定,就会进入invoke方法里面了。  service.sayHello("张三");  }
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

测试结果:

############我是JDK动态代理################
我准备说hello。
hello 张三
我说过hello了
  • 1
  • 2
  • 3
  • 4

源码跟踪

Proxy 类

// 映射表:用于维护类装载器对象到其对应的代理类缓存
private static Map loaderToCache = new WeakHashMap(); // 标记:用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object(); // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 关联的调用处理器引用
protected InvocationHandler h;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Proxy 静态方法 newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 检查 h 不为空,否则抛异常if (h == null) { throw new NullPointerException(); } // 获得与制定类装载器和一组接口相关的代理类类型对象/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, interfaces); // 通过反射获取构造函数对象并生成代理类实例/** Invoke its constructor with the designated invocation handler.*/try {final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;SecurityManager sm = System.getSecurityManager();if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {// create proxy instance with doPrivilege as the proxy class may// implement non-public interfaces that requires a special permissionreturn AccessController.doPrivileged(new PrivilegedAction<Object>() {public Object run() {return newInstance(cons, ih);}});} else {return newInstance(cons, ih);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString());} }private static Object newInstance(Constructor<?> cons, InvocationHandler h) {try {return cons.newInstance(new Object[] {h} );} catch (IllegalAccessException e) {throw new InternalError(e.toString());} catch (InstantiationException e) {throw new InternalError(e.toString());} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString());}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

动态代理真正的关键是在 getProxyClass0 方法,

getProxyClass0方法分析

方法分为四个步骤: 
1,对这组接口进行一定程度的安全检查 
检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。

2,从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到 loaderToCache。 
loaderToCache存放键值对(接口名字列表,动态生成的代理类的类对象引用)。当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。标记 pendingGenerationMarke 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。

/** Find or create the proxy class cache for the class loader.*/Map cache;synchronized (loaderToCache) {cache = (Map) loaderToCache.get(loader);if (cache == null) {cache = new HashMap();loaderToCache.put(loader, cache);}}
。。。。。
do { // 以接口名字列表作为关键字获得对应 cache 值Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // 如果已经创建,直接返回return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在被创建,保持等待try { cache.wait(); } catch (InterruptedException e) { } // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待continue; } else { // 标记代理类正在被创建cache.put(key, pendingGenerationMarker); // break 跳出循环已进入创建过程break;
} while (true);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3,动态创建代理类的class对象

/*** Choose a name for the proxy class to generate.*/long num;synchronized (nextUniqueNumberLock) {num = nextUniqueNumber++;}String proxyName = proxyPkg + proxyClassNamePrefix + num;/** Verify that the class loader hasn't already* defined a class with the chosen name.*/// 动态地生成代理类的字节码数组byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);try {// 动态地定义新生成的代理类proxyClass = 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());}// 把生成的代理类的类对象记录进 proxyClasses 表proxyClasses.put(proxyClass, null);             
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

首先根据规则(接口public与否),生成代理类的名称,$ProxyN格式,然后动态生成代理类。 
所有的代码生成的工作都由 ProxyGenerator 所完成了,该类在rt.jar中,需要反编译

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<Void>() {  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;  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

4,代码生成过程进入结尾部分,根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程。

finally {/** We must clean up the "pending generation" state of the proxy* class cache entry somehow.  If a proxy class was successfully* generated, store it in the cache (with a weak reference);* otherwise, remove the reserved entry.  In all cases, notify* all waiters on reserved entries in this cache.*/synchronized (cache) {if (proxyClass != null) {cache.put(key, new WeakReference(proxyClass));} else {cache.remove(key);}cache.notifyAll();}}
    return proxyClass;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

InvocationHandler解析

代码参考:http://rejoy.iteye.com/blog/1627405 
通过getProxyClass0方法中生成具体的class文件的过程,定义path,讲class文件写到指定的磁盘中,反编译生成的代理class文件。 
发现在静态代码块中获取了的方法有:Object中的equals方法、Object中的hashCode方法、Object中toString方法 , 以及invoke的接口方法。

后语

至此,JDK是动态生成代理类,并通过调用解析器,执行接口实现的方法的原理已经一目了然。动态代理加上反射,是很多框架的基础。比如Spring的AOP机制,自定义前置后置通知等控制策略,以及mybatis中的运用反射和动态代理来实现插件技术等等。

文章参考: 
http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html 
http://blog.csdn.net/ykzhen2015/article/details/50312651

java动态代理原理及解析相关推荐

  1. Java 动态代理原理图解 (附:2种实现方式详细对比)

    动态代理在 Java 中有着广泛的应用,例如:Spring AOP 面向切面编程,Hibernate 数据查询.以及 RPC Dubbo 远程调用等,都有非常多的实际应用@mikechen 目录 Ja ...

  2. java动态代理原理解析

    总结:一.应用:1.要代理的类必须有对应实现接口.2.被增强的代码要实现invocationHandle接口,实现接口的invoke方法,在方法里添加增强代码和通过调用method.invoke( p ...

  3. java动态代理原理

    代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 通过 ...

  4. Java动态代理原理分析

    JDK动态代理原理分析 interface Foo {void foo(); }class Target implements Foo {@Overridepublic void foo() {Sys ...

  5. 动态代理——CGLIB动态代理原理示例解析

    觉得可以的话点个关注,转个发呗,陆续奉上干货~~~~ 前文我们讲解了JDK动态代理的原理(动态代理--JDK动态代理原理),今天我们来看看CGLIB动态代理是如何实现,最后我们总结下JDK动态代理和C ...

  6. Java 动态代理 原理解析

    概要 AOP的拦截功能是由java中的动态代理来实现的.说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常 ...

  7. java动态代理原理剖析

    首先来谈为什么需要代理. 比如我有一个接口如下 public interface Moveable {void move();} 它有一个move方法.现在我有这样一个需求,当我要通过子类实现该接口调 ...

  8. Java 动态代理原理

    java.lang.reflect.Proxy#newProxyInstance public static Object newProxyInstance(ClassLoader loader,Cl ...

  9. java动态代理实现步骤解析

    当我们使用代理方法的时候,我们希望实现以下目的: 代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象.代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象.同时 ...

最新文章

  1. Android SearchView介绍及搜索提示实现
  2. OpenCV之feature2d 模块. 2D特征框架(1)Harris 角点检测子 Shi-Tomasi角点检测子 定制化创建角点检测子 亚像素级的角点检测 特征点检测
  3. adapt和adopt的区别_脸盲了,adopt和adapt要如何区分?
  4. C++未定义行为-数组越界
  5. ASP.NET MVC5+EF6+EasyUI 后台管理系统(52)-美化EasyUI皮肤和图标
  6. Derangement(AtCoder-3525)
  7. JAVA实训心得体会(精选4篇)
  8. 中兴b860刷机运行Linux,整理 B860A 刷机,安装第三方,升降固件,进recovery
  9. 2017第34届广州国际特许连锁加盟展览会会刊(参展商名录)
  10. 更多数学趣题:走迷宫
  11. iPhone内存溢出——黑白苹果
  12. ConCare: Personalized Clinical Feature Embedding via Capturing the Healthcare Context
  13. 2.1 电子计算机的兴起
  14. HTML——制作新闻网页
  15. CSS3多列布局 columns 弹性布局 flex
  16. python数据分析建模-十分钟搞懂“Python数据分析”
  17. 前端知识的浅薄了解2
  18. 【C语言入门小游戏】三子棋
  19. 完美主义的一点点体现
  20. 苹果笔记本中挖矿木马

热门文章

  1. 16.枚举中的option和result.rs
  2. 视音频编解码学习工程:TS封装格式分析器
  3. 详解分布式一致性机制
  4. 我用 Redis 干掉了一摞简历
  5. IO 密集型服务 性能优化实战记录
  6. 你真的了解泛型 Generic 嘛?
  7. Django环境搭建
  8. LiveVideoStack线上交流分享 (十六) —— 爱奇艺剧场直播云端混流方案
  9. Hadoop之NameNode和SecondaryNameNode工作机制详解
  10. 2019 WAIC | 腾讯张正友:人工智能的热与酷