代理模式的作用是:为其他对象提供一种代理,以控制对这个对象的访问。在某些情况下,一
个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
编程与设计思想:掌握代理模式对于Spring AOP的学习是至关重要的,甚至比Spring AOP
本身的学习还要重要。
代理模式的一般角色:
抽象角色:声明真实对象和代理对象的共同接口
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象
提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行
真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
静态代理:
抽象角色:定义了一个真实对象和代理对象的方法
public abstract class Subject {
public abstract void request();
}
真实角色:实现了抽象角色中的共同方法
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("from real subject");
}
}
代理角色:
public class ProxySubject extends Subject {
private Subject realSubject; //持有真实对象的一个引用,通过它可以调用真实对象的方法
public ProxySubject(Subject realSubject) { //通过构造函数获得真实对象
this.realSubject = realSubject;
}
@Override
public void request() {
preRequest(); //代理对象添加在真实对象具体操作前的操作
realSubject.request(); //真实对象的具体操作
postRequest(); //代理对象添加在真实对象具体操作后的操作
}
private void preRequest() {
System.out.println("pre request");
}
private void postRequest() {
System.out.println("post request");
}
}
客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理RealSubject类,
同样达到目的,同时还封装了其他方法(preRequest(),postRequest()),可以处理一些其他问题。
如果按照上述的方法使用代理模式,那么真实角色必须事先已经存在,并将其作为代理对象的
内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨
胀;此外如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来
解决。
动态代理类
Java动态代理类位于java.lang.reflect包下,一般主要涉及到一下两个类
1、Interface InvocationHandler:该接口中仅定义了一个方法
public object invoke(Object obj, Method method, Object[] args);
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的
request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
2、Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容
1) protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。Proxy类中
持有 InvocationHandler接口的一个实例。
2) static Class getProxyClass(ClassLoader loader, Class[] interfaces):获得一个代理类,
其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
3)static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHander h):
返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的
在Subject 接口中声明过的方法)
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它是你必须提供一组
interface给它,然后该类就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface
中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工作,在
生成它的实例时你必须提供一个handler,由它接管实际的工作。
抽象角色和真实角色和上面一样,不再添加。
代理角色:该代理角色持有真实角色引用,并且可以是任意真实角色,即这一个代理类可以代理
Subject的所有真实角色
public class ProxySubject implements InvocationHandler {
private Object realSubject;
public ProxySubject(Object realSubject) {
this.realSubject = realSubject;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("pre processing");
method.invoke(realSubject, args);
System.out.println("post processing");
return null;
}
}
测试类代码:
public class Client {
public static void main(String[] args) {
RealSubject subject = new RealSubject();
InvocationHandler ih = new ProxySubject(subject); //将真实对象实例传给代理对象,那么可以改变这个真实对象
Class<?> clazz = subject.getClass(); //获取真实对象的Class对象
Subject s = (Subject)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), ih);
s.request(); //调用该方法时,流程转到ih参数的invoke方法,method参数就是s调用的方法名,参数就是s调用的方法的参数
}
}
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject
接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常
灵活的动态代理关系。
动态代理步骤:
1、创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2、创建被代理的类以及接口
3、通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)创建一个代理。
4、通过代理调用方法