动态代理
动态代理
1 学习动态代理的目的
动态代理技术都是在框架中使用,例如:Struts1、Struts2、Spring和Hibernate中都使用了动态代理技术。如果你不想自己写个框架,那么你基本上是用不上动态代理技术的。
我们学习动态代理技术的目的是为了更好的理解框架内部的原理,也就是说是为了将来我们学习框架打基础!
动态代理技术有点小难度!而且明白了动态代理技术可能一时也想不到它适合在什么情况下使用它。这些东西都会在学习框架时渐渐明白。
2 运行时实现指定的接口
想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口。这才是实现某个接口:
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()");
}
}
上面的代码对我们来说没有什么新鲜感,我们要说的是动态代理技术可以通过一个方法调用就可以生成一个对指定接口的实现类对象。
Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口。没错,返回值mi是MyInterface接口的实现类。你不要问这个类是哪个类,你只需要知道mi是MyInterface接口的实现类就可以了。你现在也不用去管loader和h这两个参数是什么东东,你只需要知道,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()方法!
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();
}
InvocationHandler接口只有一个方法,即invoke()方法!它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。
想象中的类:
class X implements MyInterface {
private InvocationHandler h;
public X(InvocationHandler h) {
this.h = h;
}
public void fun1() {
h.invoke();
}
public void fun2() {
h.invoke();
}
}
注意,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了。
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
5 动态代理的用途
动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。
下面我们用一个例子来说明动态代理的用途!
我们来写一个Waiter接口,它只有一个serve()方法。MyWaiter是Waiter接口的实现类:
public interface Waiter {
public void serve();
}
public class MyWaiter implements Waiter {
public void serve() {
System.out.println("服务...");
}
}
现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”。
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();
}
}
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;
}
}