大家好,我是馆长!今天开始我们就要进入结构型模式的讲解和整理了。在开始具体的讲解之前,我们再次介绍下结构型模式含义和涉及到的相关模式。
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。其中包含:代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式。今天我们就讲一下今天的主角:代理模式。
代理模式(Proxy Pattern)
定义
代理模式是一种结构型模式,是为某对象提供一种代理以控制对该对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。最经典的示例就是 Spring AOP的应用。可分为:静态代理和动态代理。
解决问题
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
实现
代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。
结构
主要角色如下:
1.抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
2.真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3.代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
4.客户端(Client):使用抽象主题接口来操作真实主题或代理主题,不需要知道具体是哪一个实现类。
注意:
1、真实主题和代理类都是实现抽象主题(Subject)类。也就是说真实 主题类和代理类拥有共同的基类或者接口(抽象主题(Subject)类)。
2、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口(共同接口)。
3、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
代码实现:静态代理
//抽象主题类 Subject
public interface Subject {
void Request();
}
//真主题类:HuaWeiSubject
public class HuaWeiSubject implements Subject{
@Override
public void Request() {
System.out.println("HuaWeiSubject 真实主题 Request");
}
}
//代理主题类 ProxySubject
public class ProxySubject implements Subject {
private HuaWeiSubject realSubject;
private void preRequest() {
System.out.println("代理主题的前置处理:preRequest");
}
private void afterRequest() {
System.out.println("代理主题的后置处理:afterRequest");
}
@Override
public void Request() {
if (realSubject == null) {
realSubject = new HuaWeiSubject();
}
preRequest();
realSubject.Request();
afterRequest();
}
}
//模拟客户类:Client
public class ClientDemo {
public static void main(String[] args) {
ProxySubject proxySubject = new ProxySubject();
proxySubject.Request();
}
}
代码实现:动态代理
jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
动态代理步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法。
2.创建被代理的类以及接口
3.通过Proxy的静态方法newProxyInstance创建一个代理
4.通过代理调用方法
//抽象主题类 Subject
public interface Subject {
void Request();
}
//真实主题类:HuaWeiSubject
@Data
public class HuaWeiSubject implements Subject {
@Override
public void Request() {
System.out.println("HuaWeiSubject 真实主题 Request");
}
}
//动态代理类
@Data
public class DynamicProxy implements InvocationHandler {
private Subject subject;
DynamicProxy(Subject subject) {
this.subject = subject;
}
/**
* invoke()方法同样有三个参数:
* 1.Object proxy 动态代理类的引用,通常情况下不需要它。但可以使用getClass()方法,得到proxy的Class类从而取得实例的类信息,如方法列表,annotation等。
* 2.Method method 方法对象的引用,代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等
* 3.Object[] 对象数组,代表被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--before Request...");
method.invoke(subject, args);
System.out.println("--before Reqeust...");
return null;
}
}
//模拟客户类
public class ClientDemo {
public static void main(String[] args) {
DynamicProxy handler = new DynamicProxy(new HuaWeiSubject());
/**
* Proxy.newProxyInstance()方法有三个参数:
* 1.ClassLoader loader:类加载器(Class Loader)
* 2.Class<?>[] interfaces:需要实现的接口数组
* 3.InvocationHandler h:InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的关键所在
*/
Subject huaWeiSubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class},
handler);
huaWeiSubject.Request();
}
}
其他实现方式:
Cglib动态代理:它是基于继承被代理类的方式实现的,不强制要求实现任何接口。Cglib动态代理利用了ASM框架,这是一种Java字节码操控框架,允许在不重新编译原始类的情况下动态生成新的类。Cglib动态代理的工作原理是在运行期生成一个新的子类,这个子类覆盖了原始类的方法,并在方法调用前和后添加额外的逻辑。这种代理方式适合于那些没有实现接口但希望对其所有方法进行增强的情况。具体的实现方式和代码建设请各位看官自行查阅。
扩展
当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型做法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。
好了,关于代理模式的说明,馆长就先讲到这里。谢谢各位看官!!
23 种设计模式不是孤立存在的,很多模式之间存在一定的关联关系,在大的系统开发中常常同时使用多种设计模式,或者模式与模式之间的组合进行生成更加强大的程序功能。