代理模式
简介
由于一些原因,需要给某个对象提供一个代理以此来控制对该对象进行访问,此时访问该对象需要一些限制,故不适合直接引用该对象,因为需要代理对象作为访问目标对象的一个媒介。
java中的代理按照代理类生成的时机不同又分为静态代理和动态代理两种。静态代理代理类在代码的编译器就生成,但是动态代理代理类是在java运行时动态生成。其中动态代理又有JDK代理和CGlib代理这两种。
代理模式的结构
- 抽象主题(Subject)类:通过接口或者抽象类声明真实主题和代理对象的业务方法
- 真实主题(Real Subject)类:实现类抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象
- 代理类(proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用。它可以
访问和扩展真实主题的功能
静态代理
由上图我们可以看到Proxy 实现了Subject的同时又聚合了RealSubject类,Proxy实现的功能,其实是调用了RealSubject的功能,同时又可以在其前后添加一些其没有的功能,对其进行
增强。而Client是对Proxy进行访问,不是直接对RealSubject进行访问,对其有一个保护作用。
JDK动态代理
JDK动态代理,java中提供了一个动态代理类Proxy,该Proxy与上述说的代理对象的类不同,而是提供了一个创建代理对象的静态方法(newProxyInstance())来获取代理对象。
//Subject抽象主题类
public interface SellTickets {
void sell();
}
//RealSubject 真实主题类
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
// 代理工厂,用来创建代理对象
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
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;
}
}
以上可知JDK代理的对象的类需要实现一接口,如果没有接口是不能使用JDK代理。
CGlib动态代理
没有接口是不能使用JDK代理,但是可以使用CGlib代理。CGLib原理是动态生成被代理类的子类。
在JDK1.8,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。
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;
}
}
其中ProxyFactory实现了MethodInterceptor,函数增强的方法是在intercept()函数中构写。
其中CGlib代理中内部实现逻辑是和JDK差不多,只不是是把真实对象类当作接口(类比但不相同)