Java的代理模式详细解析

应用场景

在设计模式中有一条开闭原则:对扩展开放,对修改关闭。在不改变已有对象的情况下,对该对象的功能进行扩充时,我们可以用到代理模式。

静态代理

有一个类 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());
	}

结果为:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值