Java动态代理中有两个重要的接口跟类,即 InvoctionHandler(Interface)、DynamicProxy(class),这个接口跟类使我们应用动态代理时必须要用的。每一个动态代理类都必须去实现 InvoctionHandler 这个接口,而且每个代理类的实例都必须关联一个 Hanlder 通过代理对象调用被代理对象中的方法时,其实是代理对象委托 InvoctionHandler 中的 invoke() 方法去调用被代理对象中的方法。invoke()方法的语法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable 其中三个参数的具体说明如下: proxy: 指代我们所代理的那个真实对象 method: 指代的是我们所要调用真实对象的某个方法的Method对象 args: 指代的是调用真实对象某个方法时接受的参数
Proxy 这个类提供了许多方法,其中我们最常用到的是
newProxyInstance
这个方法,这个方法作用就是得到一个动态代理的实例:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
我们来看一个例子,手机按我们定义了一个 Subject 接口,这个接口声明了真实对象和代理对象的共同接口,作用是在任何可以使用真实对象的时候都可以使用代理对象:
package proxy1;
public interface Subject {
void rent();
void Hello(String str);
}
接下来要定义一个真实类来实现这个接口:
package proxy1;
public class RealSubject implements Subject {
@Override
public void rent() {
System.out.println("I want to rent a house!");
}
@Override
public void Hello(String str) {
System.out.println("Hello " + str);
}
}
这个真实类中具体实现了抽象接口中定义的方法。
接下来就要定义动态代理类了,这个动态代理类必须实现 InvocationHandler 接口:
package proxy1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object subject;
public DynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before rent a house,you need to know the proxyObject name.");
System.out.println("The proxyObject name is: " + method.getName());
method.invoke(subject, args);
System.out.println("Now you can live in your new house.");
return null;
}
}
代理类中含有对真实类的引用,从而可以在任何时刻操作真实对象。代理类提供一个与真实类相同的接口,以便在任何时候都可以代理真实类,控制对真是类引用,负责在需要的时候创建真实类对象或者删除真实类对象。代理类通常在将客户端调用传递给真实类之前或者之后,都要执行特定操作,比如日志记录等等,而不是单纯地将调用传递给真实类。
最后我们还要定义一个客户端:
package proxy1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Subject subject=new RealSubject(); //创建一个真实对象的引用
InvocationHandler handler=new DynamicProxy(subject); //将真实对象的引用传递给代理对象
Subject str=(Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(),
subject.getClass().getInterfaces(), handler); //代理对象调用真实对象的方法,通过 Proxy.newProxyInstance 创建的
//代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,
//也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,
//以$开头,proxy为中,最后一个数字表示对象的标号。
str.rent();
str.Hello("Tom");
}
}
控制台输出:
Before rent a house,you need to know the proxyObject name.
The proxyObject name is: rent
I want to rent a house!
Now you can live in your new house.
Before rent a house,you need to know the proxyObject name.
The proxyObject name is: Hello
Hello Tom
Now you can live in your new house.
str.rent();
str.Hello("Tom");
这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行:
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
// 在代理真实对象前我们可以添加一些自己的操作
System.out.println("Before rent a house,you need to know the proxyObject name.");
System.out.println("The proxyObject name is: " + method.getName());
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject, args);
// 在代理真实对象后我们也可以添加一些自己的操作
System.out.println("Now you can live in your new house.");
return null;
}
调用的就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。