代理模式(Proxy Pattern),Java 常见的设计模式之一,是 GoF 的 23 种设计模式中的一种结构型设计模式。 代理模式 是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。代理对象 具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理(为真实对象提供代理,然后供其他对象通过代理访问真实对象)。 ~ 本篇内容包括:关于代理模式、代理(静态代理)实现 Demo、代理(动态代理)实现 Demo、代理(CGLIB 动态代理)实现 Demo
代理模式(Proxy Pattern),Java 常见的设计模式之一,是 GoF 的 23 种设计模式中的一种结构型设计模式。
代理模式 是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。代理对象 具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理(为真实对象提供代理,然后供其他对象通过代理访问真实对象)。
Java 中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在 Java 运行时动态生成。动态代理又有 JDK 代理和 CGLib 代理两种。
适配器模式一般包含三种角色:
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
# 代理模式的优点:
# 代理模式的缺点:
# Subject 抽象主题
interface Subject {
void Request();
}
# RealSubject 真实主题
class RealSubject implements Subject {
public void Request() {
System.out.println("访问真实主题方法...");
}
}
# Proxy 代理
class Proxy implements Subject {
private RealSubject realSubject;
public void Request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.Request();
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
# 测试
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
}
}
由于静态代理只能够对一种类型(接口)进行代理,如果想要对多种类型进行代理的话就需要创建多个代理类,为了弥补了静态代理的不足,从而出现了动态代理,使用反射技术实现
反射包 java.lang.reflect, 里面有三个类:InvocationHandler, Method, Proxy。
# SellTickets 抽象主题
public interface SellTickets {
void sell();
}
# TrainStation 真实主题
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
# ProxyFactory 代理工厂,用来创建代理对象
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/*
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
//执行真实对象
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
# 测试
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
同样是上面的案例,我们再次使用 CGLIB 代理实现。
如果没有定义 SellTickets 接口,只定义了 TrainStation(火车站类)。很显然 JDK 代理是无法使用了,因为 JDK 动态代理要求必须定义接口,对接口进行代理。
CGLIB 是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
//火车站
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
//代理工厂
public class ProxyFactory implements MethodInterceptor {
private TrainStation target = new TrainStation();
public TrainStation getProxyObject() {
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer =new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
/*
intercept方法参数说明:
o : 代理对象
method : 真实对象中的方法的Method实例
args : 实际参数
methodProxy :代理对象中的方法的method实例
*/
public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
return result;
}
}
//测试类
public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=28bx1ao8mslcw