设计模式(十三)----代理模式

概述

代理模式(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();
	}
}

输出结果
设计模式(十三)----代理模式_第1张图片
注:动态代理中代理对象不需实现接口,但目标对象必须实现接口。

Cglib代理

上面的两种方式都需要目标对象实现一个接口,但有时候目标对象只是一个单独的对象,并没有实现任何接口,这时就可以使用以目标对象子类的方式实现代理,叫做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修饰的方法无法进行代理。

总结

代理模式主要用于对一个类访问进行控制。
优点:职责清晰,高扩展性。
缺点:由于在客户端和真实主题之间增加了代理对象,所以有些类型的代理模式可能会造成请求的处理速度变慢。实现代理模式需要额外的工作,有些代理模式的实现较为复杂。
与适配器模式的区别:适配器模式主要用于改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
与装饰器模式的区别:装饰器模式用于增加功能,代理模式是为加以控制。

你可能感兴趣的:(java,设计模式,Java设计模式)