代理模式
什么是代理模式:
例如,有A B C三个类,A原来可以调用C类的方法,现在因为某种原因C类不允许A类调用其方法,但B类可以调用C类的方法,A类通过B类调用C类的方法。这里B是C的代理,A通过代理访问C。
现实生活中也有各种代理模式相关的例子:比如一个公司老板(目标类),普通人(客户类)想找他签文件,一般是不能直接找到老板的,而是通过去找老板的经纪人(代理类),让经纪人帮忙找老板签文件。又比如,平时租房子,我们(客户类)一般都是无法直接找到房东(目标类)租房的,房东会把房子代理给中介(代理类),中介负责帮房东把房子租出去。如果我们想要租房,就会去找中介,然后中介再去和房东确认。
作用
1、功能增强:在原有的功能上,增加了额外的功能
2、控制访问:目标类不让访问其他类访问,只让代理类访问
静态代理
优点:实现简单,容易理解
缺点:
1、当目标类增加了,代理类也要增加
2、当接口中的功能增加或修改,会影响众多的实现类,代理类,他们都需要同步修改
假设小明想买一件耐克球衣,他可以在淘宝店铺上进行购买,然后淘宝店铺又会去耐克工厂拿货,拿货价是100元一件,因为淘宝商家也要赚钱,所以加价了50,所以小明买下一件球衣的价格是150。这个例子中淘宝商家就是代理。
下面用静态代理实现这个例子:
1、定义一个卖衣服的接口
/**
* 定义一个卖衣服的接口
*/
public interface SellClothes {
float sell(int count);
}
2、定义一个目标类,实现卖衣服的这个接口,这里的目标类就是耐克工厂,在这里卖一件衣服的价格是100元
/**
* 定义一个目标类,实现卖衣服的这个接口
* 耐克工厂实现了卖衣服这个接口,它可以自己卖衣服,
* 在工厂这里卖一件衣服,是100元
*/
public class NikeFactory implements SellClothes {
@Override
public float sell(int count) {
System.out.print("目标类,执行sell目标方法。价格是:" + 100f * count);
return 100f * count;
}
}
3、定义一个代理类,实现买衣服这个接口,这里的代理类就是淘宝店铺,店铺这里卖一件衣服是150元
/**
* 定义一个代理类,实现卖衣服的这个接口
* 淘宝店铺实现了卖衣服这个接口,它从耐克工厂拿货,加价再卖出去
*/
public class TaoBaoShop implements SellClothes {
//持有目标类对象(跟耐克工厂有合作)
SellClothes nikeFactory = new NikeFactory();
@Override
public float sell(int count) {
//去耐克工厂拿货
float price = nikeFactory.sell(count);
//在拿货价的基础上每件衣服加价了50元,再卖出去
System.out.print("代理类,执行sell目标方法,并对价格进行加价。最终价格是:" + (float) (price * (1.5)));
return (float) (price * (1.5));
}
}
4、创建代理对象,执行目标方法
public static void main(String[] args) {
SellClothes taoBaoShop = new TaoBaoShop();
taoBaoShop.sell(10);
}
5、输出结果如下:
动态代理
在静态代理中,目标类很多的时候,可以使用动态代理,避免静态代理的缺点。
动态代理中目标类即使很多,代理类数量可以很少,当你修改了接口中的方法时,不会影响代理类
在程序执的过程中,使用jdk反射机制,创建代理对象,并动态的指定要代理的目标类。
不需要定义代理类的.java文件——其实就是jvm运行时,动态创建class字节码并加载到jvm中
动态代理有两种实现方式:
1、jdk动态代理
缺点:要求目标类必须有接口,如果没有,则无法使用该方式。
假设小明想买一件耐克球衣,他可以在淘宝店铺上进行购买,然后淘宝店铺又会去耐克工厂拿货,拿货价是100元一件,因为淘宝商家也要赚钱,所以加价了50,所以小明买下一件球衣的价格是150。这个例子中淘宝商家就是代理。
下面用jdk动态代理实现这个例子:
1、定义一个卖衣服的接口
/**
* 定义一个卖衣服的接口
*/
public interface SellClothes {
float sell(int count);
}
2、定义一个目标类,实现卖衣服的这个接口,这里的目标类就是耐克工厂,在这里卖一件衣服的价格是100元
/**
* 定义一个目标类,实现卖衣服的这个接口
* 耐克工厂实现了卖衣服这个接口,它可以自己卖衣服,
* 在工厂这里卖一件衣服,是100元
*/
public class NikeFactory implements SellClothes {
@Override
public float sell(int count) {
System.out.print("目标类,执行sell目标方法。价格是:" + 100f * count);
return 100f * count;
}
}
3、创建一个InvocationHandler,完成代理对象的功能——去耐克工厂拿货,并加价50元
/**
* 定义一个InvocationHandler实现类,在这里完成代理类要实现的功能
*/
public class MySellHandler implements InvocationHandler {
private Object target = null;
/**
* 目标对象是动态的,所以要从外部传进来。
* 传入的类是什么,就给什么类创建代理,这就是动态代理
*/
public MySellHandler(Object o) {
target = o;
}
/**
* 定义一个InvocationHandler实现类,在这里完成代理类要实现的功能
*/
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
//目标方法的返回值
Object res = null;
//执行目标方法,传参:target:目标类实例,args:目标方法所需参数
res = method.invoke(target, args);
//执行一些额外的操作,这里是对原价格进行加价
if (res != null) {
Float price = (Float) res;
res = price + 50f;
}
System.out.print("代理类,执行sell目标方法,并对价格进行加价。最终价格是:" + res);
return res;
}
}
4、使用Proxy类的静态方法,创建代理对象。并把返回值转为接口类型。
public static void main(String[] args) {
//1、创建目标类对象——耐克工厂
SellClothes nikeFactory = new NikeFactory();
//2、创建InvocationHandler对象
InvocationHandler handler = new MySellHandler(nikeFactory);
//3、创建代理对象(淘宝代理)
SellClothes taoBaoProxy = (SellClothes) Proxy.newProxyInstance(
//目标类的类加载器
nikeFactory.getClass().getClassLoader(),
//目标类实现的接口
nikeFactory.getClass().getInterfaces(),
//InvocationHandler对象
handler
);
//淘宝代理对象执行sell方法
taoBaoProxy.sell(1);
}
5、输出结果如下:
2、CGLIB动态代理
不要求目标类有接口,其原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。(继承目标类,创建它的子类,在子类中重写父类的同名方法,实现功能的修改)
缺点:要求目标类和里面的方法都不能是final修饰的