代理模式(Proxy Pattern),提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。
这样做的好处是,可在目标对象实现的基础上,增加额外的功能操作,也就是扩展目标对象的功能。
在此设计模式中,一个类代表了另一个类的功能,该设计模式属于结构型模式。
在代理模式中,创建具有现有对象的对象,以便向外界提供功能接口。
代理模式的关键点是,代理对象与目标对象,代理对象是目标对象的扩展,并由代理对象调用目标对象。
代理模式主要为其他对象提供一种代理以控制对这个对象的访问。
静态代理在使用时,需定义接口或父类,被代理对象与代理对象须实现相同的接口,或继承同一个父类。
实例
模拟一个保存的动作
保存动作接口
public interface SaveDao {
void save();
}
目标对象
public class SaveDaoImpl implements SaveDao {
public void save() {
System.out.println("已保存。。");
}
}
代理对象
public class SaveDaoProxy implements SaveDao {
private SaveDaoImpl target;
public SaveDaoProxy(SaveDaoImpl target) {
this.target = target;
}
public void save() {
System.out.println("开始事务。。");
target.save();
System.out.println("结束事务。。");
}
}
测试类
public class ProxyPatternDemo {
public static void main(String[] args) {
SaveDaoImpl target = new SaveDaoImpl();
SaveDaoProxy proxy = new SaveDaoProxy(target);
proxy.save();
}
}
输出结果
静态代理可在不修改目标对象功能的前提下,对目标功能进行扩展。
因为代理对象须与目标对象实现同样的接口,所以会有很多代理类。同时,一旦接口发生变化,目标对象与代理对象都需要维护。
动态代理中的代理对象,不需要实现接口,代理对象的生成,是通过JDK的API,动态在内存中构建代理对象(需指定创建代理对象/目标对象实现的接口类型)。
动态代理又叫JDK代理或接口代理。
JDK中生成代理对象的API,代理类所在包为java.lang.reflect.Proxy,JDK实现代理只需使用newProxyInstance方法,该方法需接收三个参数,完整写法为:
static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h);
该方法在Proxy类中是静态方法,三个参数分别为:
ClassLoader loader:指定当前目标对象使用的类加载器,获取类加载器的方法是固定的。
Class>[] interfaces:目标对象实现的接口类型,使用泛型方式确认类型。
InvocationHandler h:事件处理,执行目标对象方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。
实例
SaveDao和SaveDaoImpl没做修改,在此基础上新增一个代理工厂类ProxyFactory,在测试类中先建立目标对象和代理对象之间的联系,然后调用代理对象中的同名方法。
代理工厂类
public class ProxyFactory {
//维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.getProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
System.out.println("开始事务。。。");
//执行目标对象的方法
Object returnValue = method.invoke(target,args);
System.out.println("结束事务。。。");
return returnValue;
}
}
);
}
}
测试类
public class ProxyPatternDemo {
public static void main(String[] args) {
//目标对象
SaveDaoImpl target = new SaveDaoImpl();
System.out.println(target.getClass());
//代理对象
SaveDao proxy = (SaveDao)new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
proxy.save();
}
}
输出结果
注:动态代理中代理对象不需实现接口,但目标对象必须实现接口。
上面的两种方式都需要目标对象实现一个接口,但有时候目标对象只是一个单独的对象,并没有实现任何接口,这时就可以使用以目标对象子类的方式实现代理,叫做Cglib代理。
Cglib代理,也叫子类代理,是在内存当中构建一个子类对象,从而实现对目标对象功能的扩展。
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类和实现java接口,广泛被许多AOP框架使用,如Spring AOP和synaop,为它们提供方法的拦截。
Cglib包的底层是通过使用一个小块的字节码处理框架ASM来转换字节码并生成新的类。
Cglib代理实现方法:
需引入Cglib的jar文件,但Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-xxx.jar即可。
引入功能包后,就可在内存中动态构建子类。
注:代理的类不能被final修饰,否则会保错;目标对象的方法若为final/static,那么就不会拦截该方法,也就是说不会执行目标对象方法之外的额外扩展方法。
实例
目标对象类
public class SaveDaoImpl {
public void save() {
System.out.println("数据已保存。。");
}
}
Cglib代理工厂
public class ProxyFactory implements MethodInterceptor {
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//为目标对象创建代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperClass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类(代理对象)
en.create();
}
public Object intercept(Object obj,Method method,Objects args,MethodProxy proxy) throws Throwable {
System.out.println("开始事务。。");
//执行目标对象方法
Object returnValue = method.invoke(target,args);
System.out.println("结束事务。。");
return returnValue;
}
}
测试类
public class ProxyPatternDemo {
public static void main(String[] args) {
//目标对象
SaveDaoImpl target = new SaveDaoImpl();
//代理对象
SaveDaoImpl proxy = (SaveDaoImpl)new ProxyFactory(target).getProxyInstance();
//执行代理对象方法
proxy.save();
}
}
输出结果
在Spring的AOP编程中,若加入容器的目标对象有实现接口,则用JDK代理,反之,则用Cglib代理。
Cglib创建的动态代理对象比JDK创建的动态代理对象性能更高,但Cglib创建代理对象所用的时间要比JDK方式创建的要多。所以对于单例对象,因为无需频繁创建对象,用Cglib比较合适,反之使用JDK方式更为合适。
同时由于Cglib是动态创建子类的方法,对于final修饰的方法无法进行代理。
代理模式主要用于对一个类访问进行控制。
优点:职责清晰,高扩展性。
缺点:由于在客户端和真实主题之间增加了代理对象,所以有些类型的代理模式可能会造成请求的处理速度变慢。实现代理模式需要额外的工作,有些代理模式的实现较为复杂。
与适配器模式的区别:适配器模式主要用于改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
与装饰器模式的区别:装饰器模式用于增加功能,代理模式是为加以控制。