介绍
这篇文章是关于java动态代理的,它是 java 语言提供给我们主要的代理机制。
简单的说,代理就是使用自己定义的方法,包装原始对象的方法调用,在自己的方法中可以做一些额外的事情。用户直接使用代理对象完成业务请求。
动态代理允许一个具有单个方法的类为任意数量的方法对任意类进行多个方法调用。动态代理可以看做是一个「Facade」模式,但是也可以包装成一个接口的实现。在这两种特性之下, 它可以将所有方法的调用路由到某一个方法 invoke() 中。
代理并不常用于一些日常的开发任务的。动态代理经常用于框架的编程,或者只有在运行时才能确定具体的类实现的场景中。
此特性内置于标准的 JDK 中,因此不需要额外的依赖项。
Invocation Handler
首先来构建一个简单的代理,除了打印请求调用的方法之外,实际上什么也不做,并返回一个硬编码的数字。
首先创建一个实现了 java.lang.reflect.InvocationHandler 接口的的类:
1 | package com.proxy; |
上面定义了一个简单的代理,日志输出哪个方法被调用了,并且返回1。
创建 proxy 实例
现在通过类 java.lang.reflect.Proxy 类的工厂方法 newProxyInstance() 创建刚刚定义的 InvocationHandler 类实例。
1 | package com.proxy; |
输出结果为:
一月 23, 2018 11:53:54 上午 com.proxy.DynamicInvocationHandler invoke
信息: invoke method {} put
通过 lambda 表达式实现 InvocationHandler
由于 java.lang.reflect.InvocationHandler 是一个函数式接口,可以使用lambda表达式直接定义一个 Invocation Handler 。
1 | public static void main2() { |
在 main2() 方法中,定义了一个 InvocationHandler ,如果 get() 方法被调用,则返回1,否则抛出 UnsupportedOperationException 异常。
1 | Object some = map.get("some"); |
返回结果:
23
Exception in thread “main” java.lang.UnsupportedOperationException:
Unsupported method: putat com.proxy.Main.lambda$main2$0(Main.java:22)
at com.sun.proxy.$Proxy0.put(Unknown Source)
at com.proxy.Main.main2(Main.java:29)
at com.proxy.Main.main(Main.java:12)
实际应用场景
有这样一个应用场景,假设我们想记录下我们的方法的调用耗时,此时我们先定义一个Invocation Handler,来包装我们的真实对象,即:target 对象,通过反射获取到 target 对象的信息。
1 | package com.proxy; |
然后,这个代理可以被运用于各种对象类型中:
1 | package com.proxy; |
输出内容为:
一月 23, 2018 2:39:00 下午 com.proxy.TimingDynamicInvocationHandler invoke
信息: Executing “put” finished in 17728ns
一月 23, 2018 2:39:00 下午 com.proxy.TimingDynamicInvocationHandler invoke
信息: Executing “get” finished in 10575ns
一月 23, 2018 2:39:00 下午 com.proxy.TimingDynamicInvocationHandler invoke
信息: Executing “length” finished in 6531ns
something
11
总结
以上的例子,简单的介绍了 java 动态代理的使用方法和可能的应用场景。这些场景更多的使用在框架的设计之中。