1.代理模式的定义和特点
代理模式(Proxy)的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
代理模式的优点:
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 高扩展性,代理对象可以扩展目标对象的功能;
其主要缺点:
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
2.代理模式的结构与实现
1.模式的结构
- 代理模式的主要角色:
- 抽象主题(ISubject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
- 按照代理的创建时期,代理类可以分为两种:
静态代理: 由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了
动态代理: 在程序运行时运用反射机制动态创建而成
2.动态代理和静态代理优缺点
静态代理缺点:
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,加大了工作量,扩展性差
动态代理的优点:
- 接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
3.结构图
4.实现
【例】当当网代理新华出版社卖书?
测试代码:
public class Test {
public static void main(String[] args) {
//静态代理
System.out.println("静态代理:");
IPublisher proxy = new DangDangProxy();
proxy.sellBook();
//动态代理
System.out.println("\n动态代理:");
DangDangHandler handler = new DangDangHandler();
IPublisher proxy2 = (IPublisher) handler.newProxyInstance(new XinHuaPublisher());
proxy2.sellBook();
}
}
运行结果:
静态代理:
打折
卖书
赠送优惠券
动态代理:
打折
卖书
赠送优惠券
主题:
/**
* 出版社
*/
public interface IPublisher {
//卖书
void sellBook();
}
/**
* 新华出版社
*/
public class XinHuaPublisher implements IPublisher {
@Override
public void sellBook() {
System.out.println("卖书");
}
}
静态代理:
/**
* 当当代理,静态代理
*/
public class DangDangProxy implements IPublisher {
private XinHuaPublisher publisher;
public DangDangProxy() {
publisher = new XinHuaPublisher();
}
//打折
private void discount() {
System.out.println("打折");
}
@Override
public void sellBook() {
this.discount();
this.publisher.sellBook();
this.giftCoupons();
}
//赠送优惠券
private void giftCoupons() {
System.out.println("赠送优惠券");
}
}
动态代理:
/**
* 当当代理,动态代理
*/
public class DangDangHandler implements InvocationHandler {
private Object targetObject;
/**
* 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
* 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
* 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
* 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
*
* @param targetObject 根据传入的目标返回一个代理对象
* @return
*/
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
/**
* 关联的这个实现类的方法被调用时将被执行
* InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.discount();
//调用目标方法
Object object = method.invoke(targetObject, args);
this.giftCoupons();
return object;
}
//打折
private void discount() {
System.out.println("打折");
}
//赠送优惠券
private void giftCoupons() {
System.out.println("赠送优惠券");
}
}