定义
由于某些原因需要给对象提供一个代理以控制对该对象的访问,这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
模式类型
结构型设计模式
实现方法
静态代理
代码示例
//买房者
public interface Buyer {
void buyHouse();
}
//私人
public class person implements Buyer{
@Override
public void buyHouse() {
System.out.println("私人买房");
}
}
//中介代理购房
public class proxy implements Buyer{
private Buyer buyer;
public proxy(Buyer buyer) {
this.buyer = buyer;
}
@Override
public void buyHouse() {
//增强功能:快速筛选合适房屋
System.out.println("筛选合适房屋");
//增强功能:带看房
System.out.println("带看房");
buyer.buyHouse();
}
}
//客户端调用
public class client {
public static void main(String[] args) {
person p = new person();
proxy proxy = new proxy(p);
proxy.buyHouse();
}
}
客户端调用结果:
筛选合适房屋
带看房
私人买房
上述为简单的静态代理,静态代理是代理对象和目标对象都需要实现同一接口。
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:类数量变多了,并且在接口中新增方法,代理对象和目标对象都需要修改,扩展复杂。
JDK动态代理
代码示例
//买房者
public interface Buyer {
void buyHouse();
}
//私人
public class person implements Buyer{
@Override
public void buyHouse() {
System.out.println("私人买房");
}
}
//反射代理
public class ProxyInvocationHandler<T> implements InvocationHandler {
private T target;
public ProxyInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强功能");
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
return returnValue;
}
}
//客户端调用
public class client {
public static void main(String[] args) {
Buyer p = new person();
InvocationHandler proxyFactory = new ProxyInvocationHandler<Buyer>(p);
Buyer buyer = (Buyer) Proxy.newProxyInstance(Buyer.class.getClassLoader(), new Class<?>[]{Buyer.class}, proxyFactory);
buyer.buyHouse();
}
}
客户端调用结果:
增强功能
私人买房
上述则为JDK动态代理,可以发现在目标对象在新增、修改和删除方法时,代理类无需进行任何更改,并且任何的目标对象都可以使用该代理类进行代理,所有被代理方法都会在InvocationHandler中的invoke方法进行统一处理。
JDK动态代理实现步骤如下:
- 存在目标对象和其接口。
- 创建InvocationHandler并在其invoke方法中,实现代理增强的逻辑。
- 调用Proxy.newProxyInstance方法,传入3个参数,分别是目标对象的接口类加载器,目标对象接口类和InvocationHandler实例。
迷思小贴士:
JDK动态代理在使用上存在一定的局限性,首先必须要确保被目标对象存在接口,其次被代理的方法只能是在接口中被声明的方法。但JDK动态代理无需引用任何外部依赖,JAVA原生支持。 |
CGLIB代理
代码示例
//超人(目标对象)无需实现接口
public class SuperPerson {
public void buyHouse(){
System.out.println("超人买房,不需要接口");
}
}
//CGLIB代理类
public class CGProxy implements MethodInterceptor {
private Object target;
/**
* 代理方法,实现增强逻辑
*/
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("增强功能");
Object result = proxy.invokeSuper(arg0, arg2);
return result;
}
/**
* 获取代理对象
*/
public Object getProxyObject(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 设置代理对象
enhancer.setSuperclass(this.target.getClass());
// 设置回调(在调用父类方法时,回调 this.intercept())
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
}
//客户端调用代理方法
public class Client {
public static void main(String[] args) {
SuperPerson superPerson = new SuperPerson(); // 被代理的对象
// 创建代理对象(创建的代理对象可以放在容器中缓存,后续调用时获取即可)
SuperPerson proxyObject = (SuperPerson) new CGProxy().getProxyObject(superPerson);
proxyObject.buyHouse();
}
}
上述为CGLIB(Code Generation Library),是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。CGLIB无需目标对象必须有接口,但需要引入额外的依赖包。代码中MethodInterceptor.intercept对目标对象的方法进行拦截代理,在Enhancer设置目标对象,从中获取到代理对象。