设计模式之Proxy模式(学习笔记)

  • 静态代理

问题引入

我们现在有一个Car类,Car类里面有个move方法.

现在我们想每次当这个Car move的时候都记录这个Car move了多久 (在不修改Car的源码的前提下).

public interface Moveable {
	void move();
}
public class Car implements Moveable{

	@Override
	public void move() {
		System.out.println("Car Move ....");
	}
	
}


第一种方法继承:

public class CarProxyByInheritance extends Car{
	@Override
	public void move(){
		System.out.println("time start");
		long start = System.currentTimeMillis();
		super.move();
		long end = System.currentTimeMillis();
		System.out.println(end-start+"ms");
	}
}

第二种方式组合:

public class CarProxyByCombination implements Moveable{
	private Moveable m;
	public CarProxyByCombination(Moveable m){
		this.m = m;
	}
	
	@Override
	public void move() {
		System.out.println("time start");
		long start = System.currentTimeMillis();
		m.move();
		long end = System.currentTimeMillis();
		System.out.println(end-start+"ms");
	}

}


这两种方式都产生了Car对象的代理对象。

那么究竟哪种方法更好呢?

这个问题比较简单,组合更好.

假设现在有很多Moveable接口的实现类如 飞机,轮船,航母,飞碟。

如果采用继承,对于每个实现类,如果你想产生代理对象。都需要创建一个类。

这样类就产生了类爆炸的现象

而用组合,你只需改变构造方法里面的参数即可。





  • 动态代理
问题引入:假设现在这个计时功能,我不仅仅要对Moveable的实现类计时,有可能要对别的Sleepable,Eatable类的某个方法。
那么每次,都要动手写一个代理是不是过于麻烦了?而且现在是计时功能,如果是日志功能,或者别的功能呢?
有没有什么方法可以 无论你是什么功能,无论加在某个类上,无论加在某个方法上。我都动态的产生代理对象,而不去每次手写一个类呢?


动态代理的实现方式主要有两种
1.直接生成二进制码   如 cglib
2.一种是利用被代理对象实现了某个接口 如 jdk中的 Proxy , InvocationHandler



在这里以jdk中的Proxy,InvocationHandler举例

具体的实现比较复杂,说一下思想。

思想就是根据你给的一些必要的参数(被代理对象,被代理对象实现了哪些接口,添加逻辑(注:如日志,事务,权限))
导出代理类的源码,动态编译代理类,把代理类load到JVM.利用反射创建一个代理类的对象。

哇,这步骤好麻烦啊,没关系其实jdk已经给我们封装好了。


我们只要运用这个两个类,就可以得到我们想要的代理对象了。

Proxy主要接口

static Object  newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

只要调用这个方法,并且传入相应参数,Proxy会返回给你一个代理对象.

第一个参数:可以将该代理类load到JVM内存中的ClassLoader(一般情况下,写什么都行)。

第二个参数:被代理对象实现了哪些接口。

第三个参数:实现了InvocationHandler接口的类的对象(里面封装了,你要添加的功能,被代理对象的引用)。



这样创建出的代理类是什么样子的呢?
在这先不说,先看下面一个例子.

人吃东,然后我们给他加上日志.

public interface Eatable {
	void eat();
}

public class People implements Eatable{

	@Override
	public void eat() {
		System.out.println("have lunch");
	}

}


public interface MyAspect {
	void before();
	void after();
}

public class LogAspect implements MyAspect{
	public void before(){
		System.out.println("log begin ....");
	}
	public void after(){
		System.out.println("log end ....");
	}
}

public class MyInvocationHandler implements InvocationHandler{

	private Object target;
	private MyAspect aspect;
	
	
	public MyInvocationHandler(Object target, MyAspect aspect) {
		this.target = target;
		this.aspect = aspect;
	}


	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		aspect.before();
		Object ret = method.invoke(target, args);
		aspect.after();
		return ret;
	}

}

public class Test {
	
	public static void main(String[] args) {
		MyInvocationHandler mh = new MyInvocationHandler(new People(),new LogAspect());
		Class[] cs = {Eatable.class};
		Eatable ea = (Eatable)Proxy.newProxyInstance(Test.class.getClassLoader(), cs, mh);
		ea.eat();
	}

}



输出结果:
log begin....
have lunch
log end....



这个时候如果你输出ea.getClass()
结果就是$Proxy0
就说明,我们拿到了代理对象.

看完这个例子,相信你心中大概应该有个猜测了。
现在我们就说说ea引用指向的对象的类(代理类)大概是个什么样的。

public class $Proxy0 implements Eatable{
	InvocationHandler ih;
	
	public $Proxy0(InvocationHandler ih){
		this.ih = ih;
	}
	
	@Override
	public void eat() {
		try {
			Method m = Eatable.class.getMethod("eat");
			ih.invoke(this, m, null);
			//如果返回值不是void 就应该是 return ih.invoke(this, m, null);
		} catch(Throwable e) {
			e.printStackTrace();
		}
	}

}




  • 动态代理的应用
如Spring 的AOP



本人初学,如有不对请包涵。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值