JDK动态代理机制
JDK Proxy OverView
jdk的动态代理是基于接口的。必须实现了某一个或多个随意接口才干够被代理。并且仅仅有这些接口中的方法会被代理。
看了一下jdk带的动态代理api,发现没有样例实在是非常easy走弯路。所以这里写一个加法器的简单演示样例。
// Adder.java
package test;
public interface Adder {
int add(int a, int b);
}
// AdderImpl.java
package test;
public class AdderImpl implements Adder {
@Override
public int add(int a, int b) {
return a + b;
}
}
如今我们有一个接口Adder以及一个实现了这个接口的类AdderImpl,写一个Test測试一下。
// Test.java
package test;
public class Test {
public static void main(String[] args) throws Exception {
Adder calc = new AdderImpl();
int result = calc.add(1, 2);
System.out.println("The result is " + result);
}
}
非常显然。控制台会输出:
The result is 3
然而如今我们须要在加法器使用之后记录一些信息以便測试,但AdderImpl的源码不能更改,就像这样:
Proxy: invoke add() at 2009-12-16 17:18:06
The result is 3
动态代理能够非常轻易地解决问题。我们仅仅须要写一个自己定义的调用处理器(实现接口java.lang.reflect.InvokationHandler),然后使用类java.lang.reflect.Proxy中的静态方法生成Adder的代理类,并把这个代理类当做原先的Adder使用就能够。
第一步:实现InvokationHandler,定义调用方法时应该运行的动作。
自己定义一个类MyHandler实现接口java.lang.reflect.InvokationHandler,须要重写的方法仅仅有一个:
// AdderHandler.java
package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class AdderHandler implements InvocationHandler {
/**
* @param proxy 接下来Proxy要为你生成的代理类的实例,注意,并非我们new出来的AdderImpl
* @param method 调用的方法的Method实例。假设调用了add(),那么就是add()的Method实例
* @param args 调用方法时传入的參数。
假设调用了add(),那么就是传入add()的參数
* @return 使用代理后将作为调用方法后的返回值。假设调用了add(),那么就是调用add()后的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// ...
}
}
使用代理后,这种方法将代替指定的全部接口中的全部方法的运行。在本例中。调用adder.add()方法时,实际运行的将是invoke()。所以为了有正确的结果。我们须要在invoke()方法中手动调用add()方法。再看看invoke()方法的參数,正好符合反射须要的全部条件。所以这时我们立即会想到这样做:
Object returnValue = method.invoke(proxy, args);
假设你真的这么做了,那么恭喜你,你掉入了jdk为你精心准备的圈套。proxy是jdk为你生成的代理类的实例,实际上就是使用代理之后adder引用所指向的对象。由于我们调用了adder.add(1, 2),才使得invoke()运行,假设在invoke()中使用method.invoke(proxy, args),那么又会使invoke()运行。没错,这是个死循环。
然而。invoke()方法没有别的參数让我们使用了。最简单的解决方法就是,为MyHandler增加一个属性指向实际被代理的对象。
所以。由于jdk的冷幽默,我们须要在自己定义的Handler中增加下面这么一段:
// 被代理的对象
private Object target;
public AdderHandler(Object target) {
this.target = target;
}
喜欢的话还能够加上getter/setter。接着,invoke()就能够这么用了:
// AdderHandler.java
package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
class AdderHandler implements InvocationHandler {
// 被代理的对象
private Object target;
public AdderHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 调用被代理对象的方法并得到返回值
Object returnValue = method.invoke(target, args);
// 调用方法前后都能够增加一些其它的逻辑
System.out.println("Proxy: invoke " + method.getName() + "() at " + new Date().toLocaleString());
// 能够返回不论什么想要返回的值
return returnValue;
}
}
第二步:使用jdk提供的java.lang.reflect.Proxy生成代理对象。
使用newProxyInstance()方法就能够生成一个代理对象。
把这种方法的签名拿出来:
/**
* @param loader 类载入器,用于载入生成的代理类。
* @param interfaces 须要代理的接口。这些接口的全部方法都会被代理。
* @param h 第一步中我们建立的Handler类的实例。
* @return 代理对象,实现了全部要代理的接口。
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?
>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
这种方法会做这样一件事情,他将把你要代理的全部接口用一个由代码动态生成的类类实现。全部的接口中的方法都重写为调用InvocationHandler.invoke()方法。
这个类的代码相似于这样:
// 模拟Proxy生成的代理类。这个类是动态生成的,并没有相应的.java文件。
class AdderProxy extends Proxy implements Adder {
protected AdderProxy(InvocationHandler h) {
super(h);
}
@Override
public int add(int a, int b) {
try {
Method m = Adder.class.getMethod("add", new Class[] {int.class, int.class});
Object[] args = {a, b};
return (Integer) h.invoke(this, m, args);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
据api说。全部生成的代理类都是Proxy的子类。当然,生成的这个类的代码你是看不到的,并且Proxy里面也是调用sun.XXX包的api生成;普通情况下应该是直接生成了字节码。然后。使用你提供的ClassLoader将这个类载入并实例化一个对象作为代理返回。
看明确这种方法后。我们来改造一下main()方法。
// Test.java
package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) throws Exception {
Adder calc = new AdderImpl();
// 类载入器
ClassLoader loader = Test.class.getClassLoader();
// 须要代理的接口
Class[] interfaces = {Adder.class};
// 方法调用处理器,保存实际的AdderImpl的引用
InvocationHandler h = new AdderHandler(calc);
// 为calc加上代理
calc = (Adder) Proxy.newProxyInstance(loader, interfaces, h);
/* 什么?你说还有别的需求? */
// 还有一个处理器,保存前处理器的引用
// InvocationHandler h2 = new XXOOHandler(h);
// 再加代理
// calc = (Adder) Proxy.newProxyInstance(loader, interfaces, h2);
int result = calc.add(1, 2);
System.out.println("The result is " + result);
}
}
输出结果会是什么呢?
Proxy: invoke add() at 2009-12-16 18:21:33
The result is 3
对照一下之前的结果,你会发现这点东西写了我一个多小时。再来看看JDK有多懒:
target全然能够在代理类中生成。
实际方法都须要手动调用。可见代理类中重写全部的方法都仅仅有一句话:return xxx.invoke(ooo);
只是这么写也有他的理由,target自己管理,方法你爱调不调 ﹃_﹃。假设他调了,InvocationHandler接口中恐怕就须要两个方法了,还要推断返回、处理參数等等。
原文地址:http://www.cnblogs.com/huxi/archive/2009/12/16/1625899.html;
版权声明:本文博客原创文章。博客,未经同意,不得转载。
JDK动态代理机制的更多相关文章
- 深度剖析JDK动态代理机制
摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...
- 利用JDK动态代理机制实现简单拦截器
利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer { public Object execut ...
- 深度剖析java中JDK动态代理机制
https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...
- Java动态代理机制——JDK
动态代理机制是Spring AOP编程的原理基础. JDK的动态代理机制有个限制就是它只能代理实现了一个或多个接口的类.如PersonImpl得实现Person接口,才能用JDK动态代理机制. 定义一 ...
- 基于 JDK 的动态代理机制
『动态代理』其实源于设计模式中的代理模式,而代理模式就是使用代理对象完成用户请求,屏蔽用户对真实对象的访问. 举个最简单的例子,比如我们想要「FQ」访问国外网站,因为我们并没有墙掉所有国外的 IP,所 ...
- MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
我们以往使用ibatis或者mybatis 都是以这种方式调用XML当中定义的CRUD标签来执行SQL 比如这样 <?xml version="1.0" encoding=& ...
- Java动态代理机制——Cglib
上一篇说过JDK动态代理机制,只能代理实现了接口的类,这就造成了限制.对于没有实现接口的类,我们可以用Cglib动态代理机制来实现. Cglib是针对类生成代理,主要是对用户类生成一个子类.因为有继承 ...
- 大厂高级工程师面试必问系列:Java动态代理机制和实现原理详解
代理模式 Java动态代理运用了设计模式中常用的代理模式 代理模式: 目的就是为其他对象提供一个代理用来控制对某个真实对象的访问 代理类的作用: 为委托类预处理消息 过滤消息并转发消息 进行消息被委托 ...
- Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...
随机推荐
- 家庭洗车APP --- Androidclient开展 之 网络框架包介绍(一)
家庭洗车APP --- Android客户端开发 之 网络框架包介绍(一) 上篇文章中给大家简单介绍了一些业务.上门洗车APP --- Android客户端开发 前言及业务简单介绍,本篇文章给大家介绍 ...
- 如何成为游戏的生产者——第二章:如何开始你的编程(开发环境的搭建、C++语言适应)
如何成为游戏的生产者--文章二章:怎样開始你的编程 小故事:上节说到我六年级打开了那本C语言的书,然后其实我还是没看懂.好像看懂了一些printf语句.之后遇到了史无前例的困难--怎么让代码执行起来. ...
- 代理下载android4.4源代码
前提条件:需要有user, password, 代理人serverip和port(这一切都使自己的软件来完成下一个.例如freegate,它拥有一套可以自己作为一个代理server.创user/pas ...
- Cocos2d-x3.0游戏实例《不要救我》第十篇(结束)——使用Json配置数据类型的怪物
如今我们有2种类型的怪物,并且创建的时候是写死在代码里的,这是要作死的节奏~ 所以.必须可配置.不然会累死人的. ; i < size; ++i) { int id = root[i][&quo ...
- 开源远程控制RealVNC源代码中的通讯协议RFB(远程帧缓冲)(转)
在网上流传的gh0st3.6源代码中,远程桌面总是存在CPU占用率高和画面更新不及时等问题.于是想到了著名的开源远程控制RealVNC 它采用了远程帧缓存的协议(Remote Frame buffe ...
- Java IO的RandomAccessFile的使用(转)
现有如下的一个需求,向已存在1G数据的txt文本里末尾追加一行文字,内容如下“Lucene是一款非常优秀的全文检索库”.可能大多数朋友会觉得这个需求很easy,说实话,确实easy,然后XXX君开始实 ...
- 移动开发 Native APP、Hybrid APP和Web APP介绍
高速区分定义: Native App 以基于智能手机本地操作系统如IOS.Android.WP并使用原生程式(SDK)编写执行的须要用户安装使用的第三方应用程序; Web APP 以HTML+JS+C ...
- (一)IE8以下background不起作用
最近遇到了些小bug,在这里做个记录分享下.低版本的浏览器不支持新属性或者说对代码的解析方式不一样,导致最后解析出来的结果五花八门,各种浏览器之间的兼容的确是一个令开发人员头疼的事情,不过有时候换一个 ...
- 深入理解Linux修改hostname(转)
当我觉得对Linux系统下修改hostname已经非常熟悉的时候,今天碰到了几个个问题,这几个问题给我好好上了一课,很多知识点,当你觉得你已经掌握的时候,其实你了解的还只是皮毛.技术活,切勿浅尝则止! ...
- WinDbg调试.NET
WinDbg调试.NET程序入门 俗话说:万事开头难! 自从来到新公司遇到性能问题后,需要想办法解决这个问题,但是一直没有合适的性能分析工具,然后找到StevenChennet 大神帮忙,他用WinD ...