首先要说明的是,Java语言是不支持在运行时对类进行修改的,而支持运行时修改类的语言就可以采用声明式编程了,这里多扯一句跟声明式相对应的就是命令式编程。
那要怎么让Java支持动态性呢,在不支持修改类的情况下?
答案就是代理,可以运行时生成一个新的类,这个新的类就是我们要修改为的目标类,然后这个代理类代理原先的类,执行相应的功能,就好像原先的类改变了一样。
那么Java是怎么实现代理的呢,直白的讲就是怎么创建一个新类去代理一个类呢?
使用代理我们要用到Java提供两个非常重要的东西,
一个是Proxy类,
另一个是InvocationHandler接口,
两位大佬都在java.lang.reflect包中,也就是反射包,这里还没剖析源码,先看一下怎么实现代理吧。
假如我现在有一个HelloWorld类,这个类代码如下
package
dailyprg0713;
public
class
HelloWorld {
public
void
say(){
System.out.
println(
"Hello World !");
}
}
它有个say()方法,我们测试一下
package
dailyprg0713;
public
class
Test {
public
static
void
main(
String[]
args) {
new
HelloWorld().
say();
}
}
打印结果如下
没毛病
那么问题来了,如果想在运行时“修改”这个类,让他多打印一句“Hello Java World !”怎么办?
现在我们就来试一下代理,为了让调用方感觉不到代理的存在,我们应面向接口,将say()这个方法放进接口里
package
dailyprg0713;
public
interface
IHelloWorld {
public
void
say();
}
然后去实现这个接口
public
class
HelloWorld
implements
IHelloWorld{
public
void
say(){
System.out.
println(
"Hello World !");
}
}
接下来就需要去编写一个调用处理器了,这个调用处理器就是实现了InvocationHandler接口的类对象,
这个接口只有一个方法invoke(),它的特点就是,
当我们无论何时调用代理对象的方法,比如调用say()方法,调用处理器的invoke()方法就会被调用,相当于拦截了真正的方法调用,这样我们就可以在这个方法里添加或者修改我们要实现的功能了。
package
dailyprg0713;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
public
class
HelloWorld
implements
IHelloWorld{
public
void
say(){
System.out.
println(
"Hello World !");
}
}
class
HelloHandler
implements
InvocationHandler{
private
Object target;
public
HelloHandler(
Object
target){
this.target
= target;
}
public
Object
invoke(
Object
proxy,
Method
method,
Object[]
args)
throws
Throwable{
Object result
= method.
invoke(target, args);
System.out.
println(
"Hello Java World");
return result;
}
}
相应的也要在main里面创建代理类
package
dailyprg0713;
import
java.lang.reflect.Proxy;
public
class
Test {
public
static
void
main(
String[]
args) {
IHelloWorld hw
=
new
HelloWorld();
HelloHandler handler
=
new
HelloHandler(hw);
IHelloWorld proxy
= (IHelloWorld)Proxy.
newProxyInstance(Thread.
currentThread()
.
getContextClassLoader(),hw.
getClass().
getInterfaces(), handler);
proxy.
say();
}
}
我们测试一下
确实多打印了一句,而且我们没有修改HelloWorld类,用的也是say()方法。
我们可以用代理的方式给原有的类添加事务和日志。