Java InvocationHandler 与 Proxy 动态代理详解
2019-08-16 12:24:41来源:博客园 阅读 ()
Java InvocationHandler 与 Proxy 动态代理详解
Proxy 类
Proxy
类的 newProxyInstance
方法返回一个代理对象:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
loader
:一个类加载器,用null
表示使用默认的类加载器。interfaces
:一个 Class 对象数组,每个元素都是需要实现的接口。h
:一个调用处理器。
另外两个方法:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
:返回指定接口的代理类。public static boolean isProxyClass(Class<?> cl)
:如果cl
是一个代理类则返回true
。
InvocationHandler 接口
newProxyInstance
方法的第三个参数就是 InvocationHandler
, 这个接口只有一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
当调用代理类的任何一个方法的时候,invoke
方法就会被先调用,method
就是原始对象的方法,args
是该方法的原始参数。
这里的第一个参数 proxy
,他是真实对象(也就是我们要代理的对象)的代理对象,它实际上就是 Proxy
的 newInstance
方法返回的那个对象。一般情况下,我们在 invoke()
方法中都是返回真实对象方法的调用结果,当我们需要对代理对象进行连续调用(链式调用)时,可以返回这个对象。
一般情况乱下(target 为真实对象):
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(target, args);
}
需要连续调用时:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
method.invoke(target, args);
return proxy;
}
动态代理简单实现
现在,创建一个 Hero
接口,它包含一个 attack()
方法,然后使用动态代理,任何实现了 Hero
接口的英雄在调用 attack
前后分别打印出“施法前摇”和“施法后摇”。
package cloud.proxy;
public interface Hero {
void attack();
}
再创建一个施放雷神之怒的宙斯:
package cloud.proxy;
import static java.lang.System.out;
public class Zues implements Hero {
@Override
public void attack() {
out.println("雷神之怒!");
}
}
最后通过 HeroProxy
类来创建代理对象:
package cloud.proxy;
import java.lang.reflect.Proxy;
import static java.lang.System.out;
public class HeroProxy {
private void before() {
out.println("准备施法...");
}
private void after() {
out.println("施法完成。");
}
public Hero newInstance(final Class<? extends Hero> targetClass) throws Throwable {
Hero target = targetClass.getConstructor().newInstance();
return (Hero) Proxy.newProxyInstance(Hero.class.getClassLoader(), new Class[]{Hero.class},
(proxy, method, args) -> {
before();
Object result = method.invoke(target, args);
after();
return result;
}
);
}
}
当调用 HeroProxy
的 newInstance()
方法时,创建了原始对象 target
,然后通过 Proxy
类来获取代理对象,这里没有创建实体类实现 InvocationHandler
,而是直接使用了匿名类(lambda表达式)。
测试类:
package cloud.proxy;
import org.junit.Test;
public class HeroProxyTest {
@Test
public void getProxyObject() throws Throwable {
HeroProxy proxy = new HeroProxy();
// 创建 Zues 的代理对象
Hero hero = proxy.newInstance(Zues.class);
hero.attack();
}
}
执行结果:
com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 cloud.proxy.HeroProxyTest
准备施法...
雷神之怒!
施法完成。
Process finished with exit code 0
Tips
代理类在跟踪方法调用时很有用,比如我们可以为 Comparable
接口创建代理类,在compareTo
方法调用之前,打印出一些信息,这样一来,就可以查看到排序时比较的过程。
原文链接:https://www.cnblogs.com/cloudfloating/p/11341894.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- Java--反射(框架设计的灵魂)案例 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash