【引入】
生活中经常可以看到代理,想象一下我们生活中购买高铁票的,我们可以通过智行购买车票,也可以到亲自到窗口购买,这其中智行就相当于代理者,这就是代理模式。
一、代理模式
代理模式(Proxy)为其他对象提供一种代理以控制对这个对象的访问。
模式分类
二、静态代理
UML类设计图:
【代码实现】
抽象主题角色:
public abstract class Subject {
public abstract void request();
}
具体主题角色:
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("真实的请求");
}
}
代理主题角色:
public class Proxy extends Subject {
RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.request();
}
}
客户端类
public class Client {
public static void main(String[] args) {
Proxy proxy=new Proxy();
proxy.request();
}
}
三、动态代理
在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等。动态代理主要分为JDK动态代理和cglib动态代理两大类,本文主要对JDK动态代理进行探讨。
动态代理 UML类图
1、使用JDK实现动态代理
要想使用JDK动态代理,首先需要了解其相关的类或接口:
- java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口、目标接口的类加载器以及InvocationHandler便可为目标接口生成代理类及代理对象。
// 该方法用于获取指定代理对象所关联的InvocationHandler
static InvocationHandler getInvocationHandler(Object proxy)
// 该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 该方法用于判断指定类是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)
顶层接口
public interface Speakable {
public void speak(String message);
}
被代理类
import java.util.Date;
public class Person implements Speakable {
@SuppressWarnings("unused")
private String name;
@Override
public String toString() {
Date now = new Date();
System.out.println(now.getYear());
return null;
}
@Deprecated
public void speak(String message) {
System.out.println("Speak: " + message);
}
}
代理类Proxy类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyProxy implements InvocationHandler {
private Object proxied;
public MyProxy(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
method.invoke(this.proxied, args);
System.out.println("运行时间: " + System.currentTimeMillis());
return null;
}
}
测试类
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String[] args){
Person person = new Person();
Speakable speakable = (Speakable)Proxy.newProxyInstance(
Speakable.class.getClassLoader(),
new Class[]{Speakable.class}, new MyProxy(person));
speakable.speak("Lesson one!");
}
}
四、代理模式优缺点和特点
1、动态代理类的特点
包:如果所代理的接口都是 public 的,那么动态代理类将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的package 访问级别),那么它将被定义在该接口所在包(假设代理了org.ddd.reflect 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 org.ddd.reflect)。
类修饰符:动态代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;
类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类;
类继承关系:动态生成的代理类继承了类Proxy,并实现了所代理的所有接口。
2、动态代理类实例特点
每个动态代理实例都会关联一个调用处理器对象,可以通过 Proxy 提供 的静态方法 getInvocationHandler 去获得代理类实例的调用处理器对象。
在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都 会由调用处理器的 invoke 方法执行。
当代理的一组接口有重复声明的方法且该方法被调用时,代理类总是从 排在最前面的接口中获取方法对象并分派给调用处理器。
3、模式优点
被代理类可以更加专注于主要功能的实现,在一定程度上降低了系统的耦合度。
代理类可以提供额外的功能。
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了保护目标对象的作用。
4、模式缺点
由于客户端和对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求处理速度变慢;
实现代理模式需要额外的工作,有些代理模式的实现非常复杂;
五、 代理模式应用
1、远程代理:比如将“网络细节”隐藏起来,用户只考虑想要的结果即可。
2、虚拟代理:例如QQ的缩略图就是一个代理,本体就是点击后放大的图。
3、保护(安全)代理:代理类相当于网关,在某个对象本体访问某资源时,必须通过代理角色来代理访问,此时,代理类对传过来的参数“本体”进行核对,看是不是和实际本体对应,对应才可以操作。
4、智能引用:在访问对象时附加一些操作,比如对象没有引用时释放资源