定义:
为其他对象提供一种代理,以控制对这个对象的访问
解释:
代理对象在客户端和目标对象之间起到中介作用
适用场景
保护目标对象
增强目标对象
缺点
增加类的数目
增加了系统的复杂度
分类
静态代理
动态代理
CGLib代理
Spring代理选择
- Bean有实现接口时使用动态代理
- Bean没有实现接口时使用CGlib
静态代理
示例代码
public interface SellHouse {
void sell();
}
public class SellHouseOwner implements SellHouse {
public void sell() {
System.out.println("卖房子");
}
}
public class SellHouseStaticProxy implements SellHouse {
SellHouseOwner realHouseOwner;
public SellHouseStaticProxy(){
this.realHouseOwner=new SellHouseOwner();
}
public void sell() {
System.out.println("发布房源");
this.realHouseOwner.sell();
System.out.println("房子已卖掉,撤销房源");
}
}
这么一看,静态代理和前面的装饰者模式很像。
静态代理和装饰者的区分
共同点
- 都要实现与目标类相同的接口
- 这两个类中都要声明目标对象
- 不修改目标类的前提下增强目标方法
区别
- 装饰者目的就是增强目标对象,而静态代理是为保护和隐藏目标对象
- 装饰者模式中可以有很多继承于基类的子类,而代理者只有一个代理类
- 装饰者模式中子类的构造函数为父类对象,而代理类的构造函数的参数为被代理者
动态代理
示例代码
public interface RentHouse {
void rent();
}
public class RentHouseOwner implements RentHouse{
public void rent() {
System.out.println("出租房子");
}
}
public class HouseDynamicProxy implements InvocationHandler {
private Object obj;
public HouseDynamicProxy(Object obj){
this.obj=obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
System.out.println("发布房源");
method.invoke(obj, args);
if(method.getName().equals("rent")){
System.out.println("房子已租掉,撤销房源");
}else{
System.out.println("房子已卖掉,撤销房源");
}
return null;
}
}
public class Test {
public static void main(String[] args) {
// HouseStaticProxy houseStaticProxy=new HouseStaticProxy();
// houseStaticProxy.sell();
SellHouseOwner owner = new SellHouseOwner();
InvocationHandler proxy = new HouseDynamicProxy(owner);
SellHouse dynamicProxy = (SellHouse) Proxy.newProxyInstance(SellHouseOwner.class.getClassLoader(),
SellHouseOwner.class.getInterfaces(), proxy);
dynamicProxy.sell();
RentHouseOwner owner1= new RentHouseOwner();
InvocationHandler proxy1 = new HouseDynamicProxy(owner1);
RentHouse dynamicProxy1 = (RentHouse) Proxy.newProxyInstance(RentHouseOwner.class.getClassLoader(),
RentHouseOwner.class.getInterfaces(), proxy1);
dynamicProxy1.rent();
}
}
输出结果
sell
发布房源
卖房子
房子已卖掉,撤销房源
rent
发布房源
出租房子
房子已租掉,撤销房源
可以发现动态代理需要实现InvocationHandler接口,method.invoke会调用被代理类里面的对应的方法,在invoke的前后,可以加上代理自己所需要的操作。而且动态代理能够动态产生不同接口类型的代理。
静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。