由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
java中的代理机制分为静态代理和动态代理
代理模式分为三种角色:
市面上任何卖火车票软件都是通过12306来买的,这就是一个典型的代理模式,12306是目标对象,其他售票软件是代理对象。
public class ProxyPoint implements SellTicket{
// 声明12306类对象
private TrainStation trainStation = new TrainStation();
@Override
public void sell() {
System.out.println("其它app买票---可以增强");
trainStation.sell();
}
}
/**
* 卖火车票的接口
*/
public interface SellTicket {
void sell();
}
public class TrainStation implements SellTicket{
@Override
public void sell() {
System.out.println("12306卖火车票");
}
}
public class Test01 {
public static void main(String[] args) {
ProxyPoint proxyPoint = new ProxyPoint();
proxyPoint.sell();
}
}
从上面代码中可以看出测试类是直接访问ProxyPoint类对象,也就是说ProxyPoint作为目标访问对象和目标对象的中介,同时也对sell方法进行了增强。
接下来使用动态代理实现上面案例,java中提供了一个动态代理类Proxy,Proxy并不是我们上面说的代理对象类,
而是提供了一个创建代理对象的静态方法(newProxyInstance)来获取代理对象。
/**
* 获取代理对象的工厂类
* 代理类也实现了对应的接口
*/
public class ProxyFactory {
// 声明目标对象
private TrainStation station = new TrainStation();
public SellTicket getProxyObject(){
/**
* ClassLoader loader 类加载器,用于加载代理类。可以通过目标对象获取类加载器
* Class<?>[] interfaces 代理类实现的接口的字节码对象
* InvocationHandler h 代理对象的调用处理程序
*/
SellTicket proxyObject = (SellTicket) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
/**
* Object proxy:代理对象,和proxyObject对象是同一个对象
* Method method:对接口中的方法进行封装的method对象
* Object[] args:调用方法的实际参数
* 返回值:方法的返回值
*/
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//
System.out.println("代售app---增强");
// 执行目标对象的方法
return method.invoke(station, args);
}
}
);
return proxyObject;
}
}
/**
* 卖火车票的接口
*/
public interface SellTicket {
void sell();
}
/**
* 代理对象工厂,用来获取代理对象
*/
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("cglib代理");
// 要调用目标对象的方法
TrainStation invoke = (TrainStation) method.invoke(station, objects);
return invoke;
}
}
public class TrainStation {
public void sell() {
System.out.println("12306卖火车票");
}
}
public class Test02 {
public static void main(String[] args) {
// 创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
// 获取代理对象
TrainStation proxyObject = factory.getProxyObject();
// 调用代理对象中的sell方法
proxyObject.sell();
}
}
使用CGLib实现动态代理,cglib底层采用ASM字节码生产框架,使用字节码技术生成代理类,在jdk1.6之前比使用java反射效率要高。
唯一需要注意的是:cglib不能对声明为final的类或方法进行代理,因为cglib原理是动态生成被代理的子类。
在jdk1.8对jdk动态代理优化后,jdk代理效率高于cglib代理,如果有接口使用jdk代理,没有接口使用cglib代理。