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的动态代理机制的更多相关文章

  1. 基于 JDK 的动态代理机制

    『动态代理』其实源于设计模式中的代理模式,而代理模式就是使用代理对象完成用户请求,屏蔽用户对真实对象的访问. 举个最简单的例子,比如我们想要「FQ」访问国外网站,因为我们并没有墙掉所有国外的 IP,所 ...

  2. Java动态代理机制——JDK

    动态代理机制是Spring AOP编程的原理基础. JDK的动态代理机制有个限制就是它只能代理实现了一个或多个接口的类.如PersonImpl得实现Person接口,才能用JDK动态代理机制. 定义一 ...

  3. JAVA JDK的动态代理反射实现

    动态代理类使用到了一个接口InvocationHandler和一个代理类Proxy ,这两个类配合使用实现了动态代理的功能. 什么是动态代理呢?  普通代理类是指: 给每个具体类写一个代理类,以后要使 ...

  4. Java代理和动态代理机制分析和应用

    本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息 ...

  5. Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  6. Java 动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  7. 深度剖析JDK动态代理机制

    摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...

  8. Java动态代理机制详解(类加载,JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  9. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer { public Object execut ...

随机推荐

  1. shell中job管理

    参考 http://www.cnblogs.com/ggjucheng/archive/2012/10/21/2733028.html    在命令后添加一个&,如firefox &: ...

  2. 基于tiny4412的Linux内核移植 -- PWM子系统学习(八)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  3. 【web.xml】报错java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

    今天搭建新的项目,虽然在web.xml中配置了ContextLoaderListener以及IntrospectorCleanupListener 如下: web.xml中部分代码: <!-- ...

  4. Hibernate 注解@Column(nullable = false) 和 @Column(unique=true)

    unique=true是指这个字段的值在这张表里不能重复,所有记录值都要唯一,就像主键那样; nullable=false是这个字段在保存时必需有值,不能还是null值就调用save去保存入库;

  5. 如何在sublime3项目设置中设置python模块的搜索路径?ImportError: No module named *的解决办法

    问题:之前使用pycharm,用的挺溜,但是电脑配置实在不争气,pycharm启动久了,耗去大量内存,导致运行起来越来越慢,于是转向使用sublime text. 把项目从pycharm切换到subl ...

  6. Log4net的一点改进

    昨天把log4net更新了一下,发现从NUGET上安装log4net后,使用方式简化了许多.以前我在文章<log4net使用简介>中介绍过,使用log4net需要有两步前期准备工作: 在使 ...

  7. (转)MySQL的JDBC驱动源码解析

    一.背景 MySQL是一个中小型关系型数据库管理系统,目前我们淘宝也使用的也非常广泛.为了对开发中间DAO持久层的问题能有更深的理解以及最近在使用的phoenix on Hbase的SQL也是实现的J ...

  8. 云计算之路-试用Azure:制作虚拟机自定义镜像

    虚拟机自定义镜像(Image)是一个很有用的功能,可以在一台虚拟机上配置好基本的系统环境,然后做个镜像,以后创建虚拟机直接从这个镜像创建,会省掉很多重复的配置工作. 阿里云与UCloud都有这个功能, ...

  9. 【资料总结】html开发小实例

    目 录 第1章 1 HTML的基本标签 1 第2章 25 表格基础 25 第3章 53 表单和框架 53 第4章 77 CSS样式表 77 第5章 104 使用Dreamweaver制作网页 104 ...

  10. Zxing二维码扫描

    源代码地址  有问题能够加QQ:312122330 之前对于Zbar的二位码扫描.到项目上线以后才发现扫描过于灵敏.导致有时候扫描到半截就启动了. 后来翻看ZXING的源代码,没有想象的复杂,复杂的地 ...