代理
有时候我们不希望直接访问对象A,而是希望通过访问中介对象B,由B来访问A,这种方式就是我们所说的代理。这里的类A即为委托类(被代理类),B为代理类。那么使用代理的好处是什么呢?
- 隐藏委托类的实现
- 实现解耦,在不改变委托类的情况下,对代理类进行修改,实现额外的处理。
代理分为静态代理和动态代理。
静态代理
所谓静态代理就是程序运行之前代码中存在代理类,静态代理中代理类和委托类也常常继承同一父类或实现同一接口。
public interface Behaviour {
void method();
}
//委托类
public class Principal implements Behaviour{
public void method() {
System.out.println("principle.method");
}
}
//代理类
public class Agent implements Behaviour {
private Principal principal;
public Agent(Principal principal){
this.principal = principal;
}
public void method() {
principal.method();
}
}
代理类持有委托类对象的引用,通过调用委托类对象的方法实现代理。使使用者和委托类之间解耦,但是缺点是我们要提前写好代理类。
动态代理
程序运行过程中创建代理类,这种方式叫动态代理。代码中不需要专门去写一个代理类。
动态代理的实现方式
- 新建行为接口和委托类
- 新建一个类实现InvocationHandler接口,这是负责连接代理类和委托类的中间类必须实现的接口
- 通过Proxy类产生代理类对象。
先来新建行为接口和委托类
public interface Behaviour {
void method();
}
//委托类
public class Principal implements Behaviour{
public void method() {
System.out.println("principle.method");
}
}
再来写一个类实现InvocationHandler接口,
public class MyInvocation implements InvocationHandler {
private Object principal;
public MyInvocation(Object principal) {
this.principal = principal;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(principal,args);
return result;
}
}
这里解释一下InvocationHandler
接口里的public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
方法各个参数代表的意思,proxy是后面要生成的代理类对象,method表示代理对象被调用的函数。args表示代理对象被调用的函数的参数。
从上面代码我们看到,中介类持有委托类对象引用,在invoke方法中调用了委托类对象的相应方法。实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类;代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。
通过Proxy类产生代理类对象
public class Example {
public static void main(String args[]) {
Principal principal = new Principal();//构造一个委托类对象
MyInvocation myInvocation = new MyInvocation(principal);//构造中介类,并把委托类对象传入
Behaviour behaviour = (Behaviour) Proxy.newProxyInstance(Behaviour.class.getClassLoader(),
new Class[]{Behaviour.class},myInvocation);//动态生成代理类对象
behaviour.method();
}
}
这里通过Proxy.newProxyInstance(Behaviour.class.getClassLoader(),
,这个方法的三个参数,第一个参数是定义代理类的Classloader,第二个参数是委托类实现的接口列表,第三个参数是我们实现的invocaition接口实例。
new Class[]{Behaviour.class},myInvocation);//动态生成代理类对象
当我们调用代理类的方法时,behaviour.method();
,实际会调用MyInvocation
里的public Object invoke(Object proxy, Method method, Object[] args)
方法,而这个方法里又会调用委托类的相应方法,所以额外的处理逻辑一般都在这个方法里实现。