代理模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式有以下角色:
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用(目标类)
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
Java种按照代理类的生成时机不同右分为静态代理和动态代理。静态代理是编译时期就生成的。而动态代理是在Java运行过程中动态生成的。 动态代理右JDK动态代理和CGLIB动态代理两种
静态代理
【案例】
如果你要买火车票,需要到火车站买票,得先坐车到火车站,排队得操作。这样比较麻烦,而火车站在多个地方都有代售点,我们去代售点买火车篇就方便很多。这个例子就是典型得代理模式,火车站是目标对象,代售点得代理对象。
【抽象角色】
public interface SellTickets{ //抽象角色
void sell(); //买票的业务功能
}
【具体角色】
public class TrainStation implements SellTickets{ //具体角色
public void sell() {System.out.println("火车站卖票");}
}
【代理角色】
public class ProxyPoint implements SellTickets{ //代理类
private TrainStation trainStation = new TrainStation();
public void sell() {
System.out.println("代售点收取服务费");
trainStation.sell();
}
}
【客户端】
public class StaticProxyTest {
public static void main(String[] args) {
ProxyPoint proxy = new ProxyPoint();
proxy.sell();
}
}
JDK动态代理
Java中提供了一个动态代理类Proxy,Proxy通过其静态方法newProxyInstance()方法创建代理对象,在java.lang.reflect包下 里面有三个关于动态代理的类
- InvocationHandler
InvocationHandler 接口(调用处理器)就只有一个invoke()方法
invoke():表示代理对象要执行的功能代码,代理类要完成的功能就写在这个方法中 - Method
表示方法,确切的说是目标类中的方法,通过Method可以执行目标类中的方法,Method.invoke(); - Proxy
动态代理的核心对象,用来创建代理对象
public class DynamicProxyTest {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//使用factory对象的方法获取代理对象
SellTickets proxyObj = factory.getProxyObj();
proxyObj.sell();
}
}
interface SellTickets{ //抽象角色
void sell();
}
class TrainStation implements SellTickets{ //具体角色
public void sell() {System.out.println("火车站卖票");}
}
class ProxyFactory{ //代理工厂(并不是代理类)
//声明目标对象
private TrainStation trainStation = new TrainStation();
/*
newProxyInstance静态方法 创建代理对象
参数:
ClassLoader loader 类加载器,用于加载代理类。可以通过目标对象获取类加载器
Class<?>[] interfaces 目标对象实现的接口字节码对象 通过反射获取
InvocationHandler h 代理对象的调用处理程序(代理对象要完成的功能)
*/
public SellTickets getProxyObj(){ //获取代理对象
SellTickets proxyInstance = (SellTickets)Proxy.newProxyInstance(
trainStation.getClass().getClassLoader(),
trainStation.getClass().getInterfaces(),
new InvocationHandler() {
/*
Object proxy jdk创建的代理对象 和proxyInstance是同一个对象,在invoke方法中基本不用
Method method 目标类中的方法
Object[] args 调用方法的实际参数(目标方法中的参数)
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代售点收取服务费");
Object obj = method.invoke(trainStation, args); //执行目标类中的方法 也就是卖票
return obj;
}
});
return proxyInstance;
}
}
CGLIB动态代理
JDK动态代理需要创建抽象角色才可以使用。CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,因为CGLIB是第三方提供的包所以需要引入jar包依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
public class CGLIBDynamic {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory();
TrainStation proxyObj = factory.getProxyObj();
proxyObj.sell();
}
}
class TrainStation{
public void sell() {System.out.println("火车站卖票");}
}
class ProxyFactory implements MethodInterceptor {
private TrainStation trainStation = new TrainStation();
public TrainStation getProxyObj(){
//创建Enhancer对象 相当于JDK动态代理中的Proxy类
Enhancer enhancer = new Enhancer();
//设置父类字节码对象
enhancer.setSuperclass(TrainStation.class);
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation trainStation = (TrainStation) enhancer.create();
return trainStation;
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代售点收取手续费");
method.invoke(trainStation,objects);//调用目标对象的方法
return null;
}
}
CGLIB不能对声明final的类进行动态代理,因为CGLIB的原理是动态生成被代理的子类
代理模式的优缺点
- 优点
1、代理模式在客户端与目标对象之间起到了一个中介的作用和保护目标对象的作用
2、代理对象可以扩展目标对象的功能
3、代理模式能将客户端与目标对象分离,在一定程度上降低了耦合度 - 缺点
1、增加了系统的复杂度
代理模式的使用场景
1、远程代理
2、防火墙代理
3、保护代理