- 由于某些原因,需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
-
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。
- 静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。
结构
- 代理模式分为三种角色:
- 抽象主题类:通过接口或者抽象类声明真实主题和代理对象实现的业务方法;
- 真实主题类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,可以对其进行访问、控制和扩展。
静态代理
//SellTickets.java
// 抽象主题类
public interface SellTickets {
void sell();
}
// TrainStation.java
// 真实主题类
public class TrainStation implements SellTickets{
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
// ProxyPoint.java
// 代理类
public class ProxyPoint implements SellTickets {
// 声明火车站类对象
private TrainStation trainStation = new TrainStation();
@Override
public void sell() {
System.out.println("代售点收取服务费用");
trainStation.sell();
}
}
JDK动态代理
-
Java中提供了一个动态代理类Proxy,Proxy提供了一个创建代理对象的静态方法
newProxyInstance()
来获取代理对象
public class ProxyFactory {
// 声明目标对象
private TrainStation station = new TrainStation();
// 获取代理对象的方法
public SellTickets getProxyObject(){
// 返回代理对象即可
/*
ClassLoader loader : 类加载器,用于加载代理类。可以通过目标对象获取类加载器
Class<?>[] interfaces : 代理类实现的接口的Class对象
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
Object proxy: 代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用
Method method: 对接口中的方法进行封装的method对象
Object[] arg : 调用方法的实际参数
返回值: 方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代售点收取一定的服务费用(jdk动态代理)");
Object obj = method.invoke(station,args);
return obj;
}
}
);
return proxyObject;
}
}
CGLIB动态代理
- 上述的代理方式都是基于接口进行代理,如果没有定义SellTickets接口,显示无法使用JDK动态代理。
-
CGLIB是一个功能强大、高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
public class ProxyFactory implements MethodInterceptor {
// 声明火车站对象
private TrainStation station = 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("方法准备执行");
Object obj = method.invoke(station,objects);
System.out.println("方法执行了");
return obj;
}
}
-
CGLIB底层采用ASM字节码生成框架,使用字节码技术生成代理,在JDK1.6之前比使用Java反射效率要高。
-
CGLIB不能对final修饰的类或者方法进行代理。
- 有接口使用JDK动态代理,没有接口使用CGLIB代理。
动态代理与静态代理
- 动态代理最大的好处就是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,就可以更加灵活的处理,而不需要像静态代理那样在每一个类方法中进行中转。
- 如果接口增加一个方法,静态代理需要在所有的代理类和实现类中增加此方法,但动态代理不会出现该问题。
总结
- 代理模式在客户端和目标对象之间起到一个中介作用和保护作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合多;
- 但是会增加系统的复杂性。
网友评论