应用场景
在设计模式中有一条开闭原则:对扩展开放,对修改关闭。在不改变已有对象的情况下,对该对象的功能进行扩充时,我们可以用到代理模式。
静态代理
有一个类 FishSeller,其中有一个sell()方法,其定义为:
public class FishSeller {
public void sell() {
System.out.println("卖鱼喽!");
}
}
现在需要对其功能进行扩充,步骤为:
1、创建一个接口,被代理类和代理类都实现此接口;
2、代理类中保有一个被代理类的变量,并自行对其实例化(可自己new,也可传入参数);
3、在代理类的重写方法中,引用被代理类的重写方法,并可调用自身定义的方法实现功能扩充。
/*将被代理类的方法抽象成接口*/
public interface Seller {
public void sell();
}
/*被代理类实现该接口*/
public class FishSeller implements Seller {
@Override
public void sell() {
System.out.println("卖鱼喽!");
}
}
/*代理类同样实现接口*/
public class FishSellerProxy implements Seller {
private FishSeller seller;
public FishSellerProxy(FishSeller seller) {
this.seller = seller;
}
public FishSellerProxy() {}
@Override
public void sell() {
System.out.println("我是代理商!");//前拦截
if(seller==null) {
seller = new FishSeller();
}
seller.sell();//调用被代理类的方法
System.out.println("今天打5折!");//后拦截
}
}
可见,在代理类的sell()方法内,使用被代理类的方法前后都可以扩展。
缺点
1、 一旦接口增加方法,被代理对象和代理对象都要改变
2、若有很多类需要被代理,需要创建的接口和代理类也会很多
动态代理
静态代理的代理类在程序运行之前就已经编译完成,而动态代理的代理类是在运行时根据我们在Java代码中的“指示”动态生成的。
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
实现步骤:
1、将被代理类中需要被代理的方法抽象成一个接口,被代理类实现这个接口。(这一步和静态代理一样)
public interface Seller {
public void sell();
}
/*被代理类实现该接口*/
public class FishSeller implements Seller {
@Override
public void sell() {
System.out.println("卖鱼喽!");
}
}
2、创建MyHandler类实现InvocaHandler接口,重写其中invoke()方法,在此方法中可增加被代理类的新功能。
public class MyHandler implements InvocationHandler {
private Object ob; //指代被代理类,利用多态实现MyHandler类的复用
public MyHandler(Object ob) {
this.ob = ob;
}
/*在invoke方法中,proxy基本无实际用处,method代表被代理类中实现的接口中的所有方法,args代表这些方法用到的参数,
* result是method执行后返回的结果,最后需要将result返回
* 这样生成的代理类使用这些方法时才会有正确的返回结果 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是动态代理商");
System.out.println(""+proxy.getClass().getName());//返回proxy的类型看看到底是个啥
Object result = method.invoke(ob, args);//执行被代理类实现了的接口中的方法,ob是外部传进来的被代理类,args是用到的参数
System.out.println(result);
System.out.println("代理结束\n");
return result;//返回这些方法运行了的结果,这样在代理类调用接口中的方法时才会有正确的返回结果
}
}
3、通过Proxy类的newProxyInstance()方法获取代理实例。此方法中需要传入被代理类的类加载器、被代理类实现的接口和第二步创建的Handler实例。
/*
newProxyInstance()的三个参数:
第一个参数:被代理类的类加载器,用于JVM编译时加载被代理类文件;
第二个参数:被代理类所实现的接口,在这可看出被代理类中非重写接口的方法是不会被代理的
第三个参数:传入第二步建的MyHandler类实例
*/
public static void main(String[] args) {
Seller seller = new FishSeller();
Seller proxy = (Seller) Proxy.newProxyInstance(FishSeller.class.getClassLoader(),
FishSeller.class.getInterfaces(), new MyHandler(seller));
proxy.sell();
}
运行结果:
此时如果我们有其他的类需要代理,就不再需要单独创建代理类了,只需完成第一步的根据类创建接口,就可以直接调用了。
public interface Buyer {
public void buy();
public String get();
}
public class FishBuyer implements Buyer {
@Override
public void buy() {
System.out.println("妈妈叫我来卖鱼!");
}
@Override
public String get() {
return "买到了黄花鱼";
}
public String say() { //此方法未被纳入接口中,无法被代理
return "啊哦,没钱了";
}
}
测试下:
public static void main(String[] args) {
Seller seller = new FishSeller();
Seller proxy = (Seller) Proxy.newProxyInstance(FishSeller.class.getClassLoader(),
FishSeller.class.getInterfaces(), new MyHandler(seller));
proxy.sell();
Buyer proxy1 = (Buyer) Proxy.newProxyInstance(FishBuyer.class.getClassLoader(),
FishBuyer.class.getInterfaces(), new MyHandler(new FishBuyer()));
proxy1.buy();
System.out.println("今天好开心,"+proxy1.get());
}
结果为: