设计模式之代理模式笔记
- 说明
- Proxy(代理)
- 目录
- 代理模式静态代理示例类图
- 买火车票的接口
- 火车站类
- 代售点类
- 测试类
- JDK动态代理
- 买火车票的接口
- 火车站类
- 获取代理对象的工厂类
- 测试类
- CGLIB动态代理
- 火车站类
- 代理工厂类
- 测试类
- 三种代理对比
- 优缺点
说明
记录下学习设计模式-代理模式的写法。JDK使用版本为1.8版本。
Proxy(代理)
意图:为其他对象提供一种代理以控制对这个对象的访问。
结构:
其中:
- Proxy保存一个引用使得代理可以访问实体;提供一个与Subject的接口相同的接口,使代理可以用来代替实体;控制对实体的存取,并可能负责创建和删除它。
- Subject定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
- RealSubject定义Proxy所代表的实体。
适用性:
- 远程代理为一个对象在不同地址空间提供局部代表。
- 需代理根据需要创建开销很大的对象。
- 保护代理控制对原始对象的访问,用于对象应该有不同的访问权限的时候。
- 智能引用取代了简单的指针,它在访问对象时执行一些附加操作。
目录
代理模式静态代理示例类图
以该UML类图实现静态代理模式示例。
买火车票的接口
package com.example.deesign_patterns.proxy.static_proxy;
//买火车票的接口
public interface SellTickets {
void sell();
}
火车站类
package com.example.deesign_patterns.proxy.static_proxy;
//火车站类
public class TrainStation implements SellTickets{
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
代售点类
package com.example.deesign_patterns.proxy.static_proxy;
//代售点类
public class ProxyPoint implements SellTickets{
//声明火车站类对象,将TrainStation类(部分)聚合到ProxyPoint类(整体)里面
private TrainStation trainStation=new TrainStation();
@Override
public void sell() {
System.out.println("代理点收取一些服务费用");
trainStation.sell();
}
}
测试类
package com.example.deesign_patterns.proxy.static_proxy;
//测试类
public class Client {
public static void main(String[] args) {
//创建代售点类对象
ProxyPoint proxyPoint=new ProxyPoint();
//调用方法进行买票
proxyPoint.sell();
}
}
JDK动态代理
Java中提供了一个动态代理类Proxy,通过提供一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象。
买火车票的接口
package com.example.deesign_patterns.proxy.jdk_proxy;
//买火车票的接口
public interface SellTickets {
void sell();
}
火车站类
package com.example.deesign_patterns.proxy.jdk_proxy;
//火车站类
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
获取代理对象的工厂类
package com.example.deesign_patterns.proxy.jdk_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//获取代理对象的工厂类
public class ProxyFactory {
//声明目标对象
private TrainStation trainStation=new TrainStation();
//获取代理对象的方法
public SellTickets getProxyObject(){
//返回代理对象
/*
newProxyInstance方法三个参数说明:
ClassLoader loader:类加载器,用于加载代理类。可以通过目标对象来获取类加载器
Class<?>[] interfaces:代理类实现的接口的字节码对象
InvocationHandler h:代理对象的调用处理程序
*/
SellTickets proxyObject = (SellTickets)Proxy.newProxyInstance(
trainStation.getClass().getClassLoader(),
trainStation.getClass().getInterfaces(),
new InvocationHandler() {
/*
Object proxy:代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用。
Method method:对接口中的方法进行封装的method对象
Object[] args:调用方法的实际参数,如果sell方法有参数,这里的参数就是sell方法的参数,本案例中sell没有参数
返回值:方法的返回值,本案例中sell方法没有返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代售点收取一定的服务费用(jdk动态代理)");
//执行目标对象的方法,通过反射的形式调用
Object obj = method.invoke(trainStation, args);
return obj;
}
}
);
return proxyObject;
}
}
测试类
package com.example.deesign_patterns.proxy.jdk_proxy;
//测试类
public class Client {
public static void main(String[] args) {
//获取代理对象
//1.创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//2.使用factory对象的方法获取代理对象
SellTickets proxyObject = factory.getProxyObject();
//3.调用卖票方法
proxyObject.sell();
}
}
CGLIB动态代理
如果pom.xml中有如下依赖,可直接使用cglib代理
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如没有该依赖,添加这个依赖即可:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
火车站类
package com.example.deesign_patterns.proxy.cglib_proxy;
//火车站类
public class TrainStation{
public void sell() {
System.out.println("火车站卖票");
}
}
代理工厂类
package com.example.deesign_patterns.proxy.cglib_proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//代理对象工厂,用来获取代理对象
public class ProxyFactory implements MethodInterceptor {
//声明目标对象
private TrainStation trainStation=new TrainStation();
public TrainStation getProxyObject(){
//创建Enhancer对象,类似于JDK代理中的Proxy类
Enhancer enhancer=new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(TrainStation.class);
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation proxyObject = (TrainStation) enhancer.create();
return proxyObject;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代售点收取一定的服务费用(cglib代理)");
//要调用目标对象的方法,使用反射的形式调用
Object obj = method.invoke(trainStation, objects);
return obj;
}
}
测试类
package com.example.deesign_patterns.proxy.cglib_proxy;
//测试类
public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory=new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
//调用代理对象中的sell方法卖票
proxyObject.sell();
}
}
三种代理对比
jdk代理和cglib代理
- 使用cglib实现动态代理,cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk1.6之前比使用java反射效率要高。唯一需要主要的是,cglib代理不能对声明为final的类或者方法进行代理,因为cglib原理是动态生成被代理类的子类。
- 在jdk1.6,jdk1.7,jdk1.8逐步对jdk动态代理优化之后,在调用次数较少的情况下,jdk代理效率高于cglib代理,只有当进行大量调用的时候,jdk1.6和jdk1.7比cglib效率低一点,但是到jdk1.8的时候,jdk代理效率高于cglib代理。所以如果有接口使用jdk动态代理,如果没有接口使用cglib动态代理。
动态代理和静态代理
- 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
- 如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题。
优缺点
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度。
缺点:
增加了系统的复杂度。