概述
由于某些原因需要给对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
Java中代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理类在编译期生产,而动态代理类是在java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
结构
- 抽象代理类(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题类(Real Subject):实现了抽象代理类中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理类(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
静态代理
在代售点买火车票。火车站是目标对象,代售点是代理对象。
这个代码比较简单,如下:
package com.hupp.proxy.static_proxy;
/**
* 卖火车票的接口
*/
public interface SellTickets {
void sell();
}
package com.hupp.proxy.static_proxy;
/**
* 火车站
*/
public class TrainStation implements SellTickets{
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
package com.hupp.proxy.static_proxy;
/**
* 火车票代售点
*/
public class ProxyPoint implements SellTickets {
private TrainStation trainStation = new TrainStation();
@Override
public void sell() {
System.out.println("代售点收取5元服务费");
trainStation.sell();
}
}
package com.hupp.proxy.static_proxy;
public class Client {
public static void main(String[] args) {
ProxyPoint proxyPoint = new ProxyPoint();
proxyPoint.sell();
}
}
JDK动态代理
Java中提供了一个动态代理类Proxy。Proxy不代理对象,它不是代理类,而是通过创建代理对象的静态方法来获取代理对象。
package com.hupp.proxy.jdk_proxy;
/**
* 卖火车票的接口
*/
public interface SellTickets {
void sell();
}
package com.hupp.proxy.jdk_proxy;
/**
* 火车站
*/
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
package com.hupp.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 getProxyInstance() {
/*
ClassLoader loader:类加载器,用于加载代理类【代理类是程序运行过程中动态生成的】。可以通过目标对象获取类加载器。
Class<?>[] interfaces:代理类实现的接口的字节码对象
InvocationHandler h:代理对象的调用处理程序
*/
SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(
trainStation.getClass().getClassLoader(),
trainStation.getClass().getInterfaces(),
new InvocationHandler() {
/*
Object proxy:和proxyInstance是同一个对象,在本方法中基本上用不到
Method method:代表接口中被调用的方法对象
Object[] args:调用方法的实际参数
返回值:就是接口方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代售点收取5元服务费---jdk动态代理");
//执行目标对象的方法
Object obj = method.invoke(trainStation, args);
return obj;
}
});
return proxyInstance;
}
}
package com.hupp.proxy.jdk_proxy;
public class Client {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory();
SellTickets sellTickets = factory.getProxyInstance();
sellTickets.sell();
}
}
CGLIB动态代理
如果没有定义SellTickets接口,只定义了TranStation类,JDK代理就无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理。
CGLIB是第三方提供的包,需要引入jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
package com.hupp.proxy.cglib_proxy;
/**
* 火车站
*/
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
package com.hupp.proxy.cglib_proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 代理对象工厂,用来获取代理对象
*/
public class ProxyFactory implements MethodInterceptor {
private TrainStation station = new TrainStation();
public TrainStation getProxyInstance(){
//创建Enhancer类似jdk中的Proxy对象
Enhancer enhancer = new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(TrainStation.class);
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation proxyInstance = (TrainStation)enhancer.create();
return proxyInstance;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代售点收取5元服务费--cglib动态代理");
//要调用目标对象的方法
Object invoke = method.invoke(station, objects);
return invoke;
}
}
package com.hupp.proxy.cglib_proxy;
public class Client {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory();
TrainStation proxyInstance = factory.getProxyInstance();
proxyInstance.sell();
}
}
三种代理对比
- jdk代理和CGLIB代理
cglib底层采用ASM字节码生产框架,使用字节码技术生产代理类,在jdk1.6之前使用比使用java发射效率要高。唯一要注意的事cglib不能对声明为final的类或方法进行代理,因为cglib原理是动态生成被代理类的子类。
jdk1.8后jdk代理效率高于cglib代理。所以如果有接口使用jdk动态代理,如果没有借口使用cglib代理 - 动态代理和静态代理
动态代理比静态代理好。
优缺点
优点
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点
- 增加了系统的复杂度;