1、实现指定接口
想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口。这才是实现某个接口:
<span style="font-size:18px;">public interface MyInterface {
void fun1();
void fun2();
}
public class MyInterfaceImpl implements MyInterface {
public void fun1() {
System.out.println("fun1()");
}
public void fun2() {
System.out.println("fun2()");
}
}</span>
上面的代码很简单,但是如何实现通过一个方法调用就生成一个对指定接口的实现类对象?这就需要使用动态代理技术。
2、动态代理技术
<span style="font-size:18px;"> Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);</span>
上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口。返回值mi是MyInterface接口的实现类。Proxy类的静态方法newProxyInstance()方法返回的方法是实现了指定接口的实现类对象。
动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.java文件,是在运行时生成的,我们不用去关心它是什么类型的,只要知道它实现了哪些接口即可。
3、newProxyInstance()方法参数
Proxy类的newInstance()方法有三个参数:
● ClassLoader loader:它是类加载器类型,通过MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,也就是说,通过一个Class对象就可以获取到ClassLoader对象;
● Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,可以指定多个接口,上例我们只指定了一个接口:Class[] cs = {MyInterface.class};
● InvocationHandler h:它是最重要的参数,它是一个接口,叫作调用处理器,上例中mi对象是MyInterface接口的实现类对象,那么它一定是可以调用fun1()和fun2()方法,但是调用fun1()和fun2()会执行什么呢?其实无论你调用代理对象的什么方法,都是在调用InvocationHandler的invoke()方法。
<span style="font-size:18px;"> public static void main(String[] args) {
Class[] cs = {MyInterface.class};
ClassLoader loader = MyInterface.class.getClassLoader();
InvocationHandler h = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("无论你调用代理对象的什么方法,其实都是在调用invoke()...");
return null;
}
};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
mi.fun1();
mi.fun2();
}</span>
InvocationHandler接口只有一个方法,即invoke()方法,它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。
模拟实现类内部结构:
<span style="font-size:18px;">class X implements MyInterface {
private InvocationHandler h;
public X(InvocationHandler h) {
this.h = h;
}
public void fun1() {
h.invoke();
}
public void fun2() {
h.invoke();
}
}</span>
X类是我们用来理解代理对象与InvocationHandler之间的关系的,它用来说明,无论你调用代理对象的哪个方法,最终调用的都是调用处理器的invoke()方法。
4、InvocationHandler的invoke()方法
InvocationHandler的invoke()方法的参数有三个:
● Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;
● Method method:表示当前被调用方法的反射对象,例如mi.fun1(),那么method就是fun1()方法的反射对象;
● Object[] args:表示当前被调用方法的参数,当然mi.fun1()这个调用是没有参数的,所以args是一个零长数组。
invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然mi.fun1()方法是没有返回值的,所以invoke()返回的就必须是null了。
<span style="font-size:18px;"> public static void main(String[] args) {
Class[] cs = {MyInterface.class};
ClassLoader loader = MyInterface.class.getClassLoader();
InvocationHandler h = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("当前调用的方法是:" + method.getName());
return null;
}
};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
mi.fun1();
mi.fun2();
}
//结果:当前调用的方法是:fun1;当前调用的方法是:fun2</span>
5、动态代理的用途
动态代理的用途与装饰模式很相似,就是为某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换:
我们来写一个Waiter接口,它只有一个serve()方法。MyWaiter是Waiter接口的实现类:
<span style="font-size:18px;">public interface Waiter {
public void serve();
}
public class MyWaiter implements Waiter {
public void serve() {
System.out.println("服务...");
}
}</span>
现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”:
<span style="font-size:18px;">public class MainApp1 {
public static void main(String[] args) {
ClassLoader loader = MainApp1.class.getClassLoader();
Class[] cs = {Waiter.class};
Waiter target = new MyWaiter();
MyInvocationHandler h = new MyInvocationHandler(target);
Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);
waiter.serve();
}
}</span>
<span style="font-size:18px;">class MyInvocationHandler implements InvocationHandler {
public Waiter target;
public MyInvocationHandler(Waiter target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("您好!");
Object result = method.invoke(target, args);
System.out.println("很高兴为您服务!");
return result;
}
}</span>
小结:动态代理技术在框架中使用,例如:Struts1、Struts2、Spring和Hibernate中都使用了动态代理技术。学习动态代理的目的是为了更好的理解框架内部的原理,为学习框架打基础。