Java之动态代理简介
图截于《大话设计模式》

Proxy模式是常用的设计模式,其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
用户可以更加结构图,自己编码完成Proxy模式。这种实现称为静态代理。
Java提供了java.lang.reflect.Proxy类与InvocationHandler接口,配合反射,可以实现动态代理。静态代理的代理类与代理操作,都是事先编码,运行过程种无法修改代理结构。动态代理的代理与代理操作,都是在运行过程中,动态生成,可以在运行过程中,修改代理结构。
InvocationHandler类提供代理操作行为,动态构建的代理类使用该接口调用代理操作。
Proxy类主要负责动态构建代理类,有以下静态方法:
- InvocationHandler getInvocationHandler(Object proxy)
- Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
- boolean isProxyClass(Class<?> cl)
- Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
调用getProxyClass()会动态生成Proxy类的子类,并使用loader参数指定的类加载器加载;第二个参数interfaces指定该子类将要继承的接口,可以指定多个接口。
|
interface Foo { void funcA(); } Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); System.out.println(proxyClass.getName()); for (Class<?> interfaceType : proxyClass.getInterfaces()) System.out.println("\t" + interfaceType); Class<?> proxyClassB = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class, AutoCloseable.class); System.out.println(proxyClassB.getName()); for (Class<?> interfaceType : proxyClassB.getInterfaces()) System.out.println("\t" + interfaceType); |
输出如下:
|
testproxy.$Proxy0 interface testproxy.Foo testproxy.$Proxy1 interface testproxy.Foo interface java.lang.AutoCloseable |
由输出可以推测,调用Proxy.getProxyClass()后,生成子类为:
|
$Proxy* extends Proxy implements interfasces |
*为从0开始编号的整数,代表是第几个被创建的Proxy的子类。interfaces是Proxy.getProxyClass()方法的第二个不定参数。这些类的字节码创建在内存中。例如上面代码生成的子类为:
|
$Proxy0 extends Proxy implements Foo {...} $Proxy1 extends Proxy implements Foo, AutoCloseable {...} |
实现同接口的Proxy子类,只会被创建一次,拥有共同的Class实例。
|
Class<?> proxyClassA = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); Class<?> proxyClassB = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); System.out.println(proxyClassA.getName()); System.out.println(proxyClassB.getName()); |
输出如下:
|
testproxy.$Proxy0 testproxy.$Proxy0 |
由于Proxy类只有一个带InvocationHandler接口的参数,所有所以需要获取指定版本的构造函数后,传入InvocationHandler接口的实现类获取动态子类的实例。
|
Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class) .newInstance(handler); |
Proxy类提供的Proxy.newProxyInstance(),一步到位,简化了获取动态子类实例的操作。
|
Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, handler) |
fooProxy的实例为"$Proxy0 extends Proxy implements Foo {...}"类
InvocationHandler接口有一个invoke()方法需要实现,动态代理类正式调用这个方法执行代理操作。举个例子:
|
class SimpleHandler implements InvocationHandler { final private Object target; public SimpleHandler(Object target) { // *需要保留委托类的引用 this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("---Proxy before---"); // *不适用第一个参数proxy,使用构造函数保留的引用调用委托类方法 Object result = method.invoke(target, args); System.out.println("---Proxy end---"); return result; } } |
使用以下委托类和代码测试。
|
class FooImp implements Foo { @Override public void funcA() { System.out.println("FooImp"); } } // Test Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, new SimpleHandler(new FooImp())); fooProxy.funcA(); System.out.println(fooProxy.toString()); |
输出如下:
|
---Proxy before--- FooImp ---Proxy end--- ---Proxy before--- ---Proxy end--- testproxy.FooImp@4aa298b7 |
fooProxy的是实例是Proxy类的动态子类,调用该子类的funcA()方法时,实际上先调用了invoke()方法,再通过反射调用委托类的funcA()方法。
为什么toString()方法也被代理了?
实际上,Object的equal()、hashCode()也同时被代理的,这个与Proxy子类的构建过程有关,可以参考资料《JDK动态代理实现原理》。
实现InvocationHandler接口时,红色的注释指出两个疑点:
- 为什么要保留委托类实例的引用?
- 为什么不使用第一个参数proxy?
这个两个问题其实是一个问题,invoke()函数的第一个参数proxy引用的实例是Proxy类的动态子类,而不是委托类。如果使用反射调用动态子类的方法,又会再次调用invoke()函数,陷入无限循环中,直到内存移除崩溃。因此,需要保留委托类的引用,让invoke()方法可以调用到委托类的方法。这样又引出一个问题,proxy参数又什么用?
proxy参数可以做为返回值,实现方法链。具体可以参考《Understanding "proxy" arguments of the invoke method of java.lang.reflect.InvocationHandler》
Java之动态代理简介的更多相关文章
- java之动态代理
摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 这里说的静态代理可以理解为之前使用的装饰者模式,从之前使用装饰者模式实 ...
- 【设计模式】Java设计模式 - 动态代理
[设计模式]Java设计模式 - 动态代理 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 最近工作比较忙,没啥时间学习 目录 [设计模 ...
- java的动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- java中动态代理实现机制
前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...
- Java特性-动态代理
代理在开发中无处不在: 我们完成一个接口开发A,接口下有很多个实现类,这些类有些共同要处理的部分,比如每一个类都定义了接口A中的方法getXX(String name).我现在想把每次调用某个实现类的 ...
- java --- 设计模式 --- 动态代理
Java设计模式——动态代理 java提供了动态代理的对象,本文主要探究它的实现, 动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式, 动态代理 ...
- java的动态代理机制
前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...
- java中动态代理
一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...
- Java的动态代理机制详解(转)
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
随机推荐
- Netty(三):线程模型
Netty中支持单线程模型,多线程模型,主从多线程模型. 1 单线程模型 在ServerBootstrap调用方法group的时候,传递的参数是同一个线程组,且在构造线程组的时候,构造参数为1,这种开 ...
- 基于Python的安卓图形锁破解程序
安卓手机的图形锁是3x3的点阵,按次序连接数个点从而达到锁定/解锁的功能.最少需要连接4个点,最多能连接9个点.网上也有暴力删除手机图形锁的方法,即直接干掉图形锁功能.但假如你想进入别人的手机,但又不 ...
- HDU 2112 HDU Today(STL MAP + Djistra)
题目链接:HDU Today 立即集训要開始,抓紧时间练练手,最短路的基础题,第一次用STL的map 题目非常水,可是错了N遍.手贱了.本题不优点理的就是把地名转化为数字 #include <i ...
- linux uart驱动——uart platfrom 注册(三)
一:注册platform device 注册一个platfrom device一般需要初始化两个内容,设备占用的资源resource和设备私有数据dev.platfrom_data.设备的resour ...
- PHP正则匹配6到16位字符组合(且只能为数字、字母、下划线)
php正则匹配6到16位的字符串. 只允许包含数字.字母.下划线组成的6到16位字符,符合返回ture,否则返回false. 解答: 6到16位,正则可以这样写:{6,16}. 任意的字符6到16位的 ...
- Mac Security工具使用总结find-identity
Security是Mac系统中钥匙串和安全模块的命令行管理工具,(图形化工具为Keychain Access.app).钥匙串(Keychain)实质上就是一个用于存放证书.密钥.密码等安全认证实体的 ...
- 跟着实例学习设计模式(6)-生成器模式builder(创建型)
生成器模式是创建型设计模式. 设计意图:将一个复杂的类表示与其构造相分离,使得同样的构建过程可以得出不同的表示. 实例类图: IVehicleBuilder:抽象建造者.为创建一个Vehicle对象并 ...
- React中key的必要性与使用
React这个框架的核心思想是,将页面分割成一个个组件,一个组件还可能嵌套更小的组件,每个组件有自己的数据(属性/状态);当某个组件的数据发生变化时,更新该组件部分的视图.更新的过程是由数据驱动的,新 ...
- struts2_6_多个struts配置文件的应用
在大部分应用里,随着应用规模的添加,系统中Action的数量也会大量添加.导致struts.xml配置文件变的很臃肿,为了避免struts.xml文件过于庞大.臃肿,提高struts.xml文件的可读 ...
- 自己珍藏的数据库SQL基础练习题答案
一,基本表的定义与删除. 题1: 用SQL语句创建如下三张表:学生(Student),课程表(Course),和学生选课表(SC),这三张表的结构如表1-1到表1-3所示. 表1-1 Student表 ...