java学习之代理(2):静态代理和动态代理

一,代理的概念

代理是一个对象,代理对象为其他对象提供一种代理,以控制对这个对象的访问,代理对象起到中介作用,可以去掉或者增加额外的服务。

如:火车票代售点就是火车站售票处的一个代理对象,可通过访问代售点进行业务处理。

二,静态代理的2种实现方式:继承和聚合

静态代理中的代理和被代理对象在代理之前关系是确定的。它们都实现了相同的接口或者继承相同的抽象类。

下面的例子分别讲述了所有的类实现同一个接口,类对象之间是如何代理的。

1,继承

首先定义一个Movable接口

package com.jimmy.proxy;

//首先定义一个接口,接口里面有一个Move函数
public interface Movable {
	void Move();
}


定义一个Car类,首先接口,并实现Move函数

package com.jimmy.proxy;

import java.util.Random;

public class Car implements Movable{

	@Override
	public void Move() {
		//业务代码		
		try {
			Thread.sleep(new Random().nextInt(1000));  //随机睡一会
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}


定义一个Car2类,继承自Car,并重写Move()方法

package com.jimmy.proxy;

public class Car2 extends Car{
	@Override
	public void Move() {
		long startTime = System.currentTimeMillis();
		System.out.println("start moving..");
		super.Move();  //调用父类的Move()方法,这里体现了代理的思想。前后加上自己的业务代码。
		long endTime = System.currentTimeMillis();
		System.out.println("end move"+" time:"+(endTime-startTime));
	}
}

定义一个Car3类,继承自Car2,并重写Move()方法

package com.jimmy.proxy;

public class Car3 extends Car2 {
	@Override
	public void Move() {
		System.out.println("starting log");
		super.Move(); //同样,调用父类的Move()方法。前后加上自己的业务代码
		System.out.println("end log");
	}
}

现在测试上面3个类的代理关系

package com.jimmy.proxy;

public class MovableClient {
	public static void main(String[] args) {
		Movable mm = new Car3();  
		mm.Move();  //执行Car3类的Move()方法,但是都访问到了Car和Car2类的Move()方法
			    //于是,Car3的对象就是Car和Car2对象的代理对象。
	}
}

2,聚合

一个类中用另一个类的对象做成员变量。

首先还是定义Movable接口(同继承)

package com.jimmy.proxy;

//首先定义一个接口,接口里面有一个Move函数
public interface Movable {
	void Move();
}

同样定义一个Car类(同继承)

package com.jimmy.proxy;

import java.util.Random;

public class Car implements Movable{

	@Override
	public void Move() {
		//业务代码		
		try {
			Thread.sleep(new Random().nextInt(1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

定义一个时间代理类CarTimeProxy,同样实现Movable接口

package com.jimmy.proxy;

public class CarTimeProxy implements Movable{
	Movable cc = null;   //接收一个实现了Movable接口的类对象作为变量,也就是所谓的聚合
	public CarTimeProxy(Movable cc) {
		this.cc = cc;
	}

	@Override
	public void Move() {
		long startTime = System.currentTimeMillis();
		System.out.println("start moving..");
		cc.Move();  //调用被聚合对象的Move()方法,前后加上自己的业务代码。
		long endTime = System.currentTimeMillis();
		System.out.println("end move"+" time:"+(endTime-startTime));
	}
}

定义一个日志代理类CarLogProxy,同样实现Movable接口

package com.jimmy.proxy;

public class CarLogProxy implements Movable{
	Movable cc = null;  //接收一个实现了Movable接口的类对象作为变量,也就是所谓的聚合
	public CarLogProxy(Movable cc) {
		this.cc = cc;
	}

	@Override
	public void Move() {
		System.out.println("start logging..");
		cc.Move();  //调用被聚合对象的Move()方法,前后加上自己的业务代码。
		System.out.println("end log");
	}
}

现在来看,Car类,CarTimeProxy类和CarLogProxy类是平行的,均实现了Movable接口。

CarTimeProxy类和CarLogProxy类中拿到了Car类的对象。

下面测试他们之间的代理关系。

package com.jimmy.proxy;

public class MovableClient {
	public static void main(String[] args) {
		Car cc = new Car();                   //首先获得Car对象cc
		Movable mm1 = new CarTimeProxy(cc);   //将cc传入CarTimeProxy类,于是mm1就是cc的代理对象
		Movable mm2 = new CarLogProxy(mm1);   //将mm1传入CarLogProxy类,于是mm2就是mm1和cc的代理对象
		mm2.Move();
	}
}

3,静态代理中的继承和聚合那个更好?存在的问题

聚合好,代理有时需要叠加并且叠加顺序不一样,如:一种情况是先代理1,再代理2,最后代理3。另一种情况是先代理2,再代理3,最后代理1等等。

对于继承来说,因为继承模式下,代理的顺序都是写死了的,针对每一种情况,都要写一个继承类。类的个数将无限制增加。

对于聚合来说就灵活很多了,只需要根据业务逻辑,由内到外一层一层创建对象即可。

但是,现在我有其他的继承自Movable接口的类(如:bike类,motocycle类等等)想实现时间和日志代理,同样要写一系列代理类出来。类的个数将同样无限制增加。

要解决以上问题,就引入了动态代理。


二,动态代理

由静态代理我们知道,代理的部分由接口,代理对象和代理逻辑组成。

而在静态代理中,这些部分都是写死的,不能灵活变换。现在动态代理将这些部分分离出来,然后组合使用。

动态代理相比于静态代理:不需要编写代理类的代码,只需调用函数即可实现代理。灵活性高。

动态代理的使用比较套路。来看使用:

jdk的动态代理由java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类组成。

1,首先定义一个Movable接口

package com.jimmy.dynamicProxy;

public interface Moveable {
	public void run(double speed, String destnation);  //同静态代理
}


2,定义一个类,实现Movable接口

package com.jimmy.dynamicProxy;

public class Car implements Moveable{

	@Override
	public void run(double speed,String destnation) {
		System.out.println("running with speed:"+speed+"km/h"+" drive to "+destnation);
	}
}


3,现在我们就不用再写代理类了,直接由函数得到代理对象,然后直接使用。

package com.jimmy.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Proxy.newProxyInstance(loader, interfaces, h)函数返回一个Object类型的对象proxy
 * 第1个参数loader:指定代理对象的类加载器,跟被代理对象的类加载器是一样的,所以固定写法为:cc.getClass().getClassLoader()
 * 第2个参数interfaces:指定代理对象实现的接口,因为代理对象和被代理对象要实现相同的接口,所以固定写为:cc.getClass().getInterfaces()
 * 第3个参数h:是InvocationHandler接口的对象,该接口只定义了一个invoke(Object proxy, Method method, Object[] args)方法,所以在实例化该接口时要实现该方法
 * 
 * invoke(Object proxy, Method method, Object[] args)方法的三个参数:
 * 第1个参数proxy:指代理对象本身,一般用不到
 * 第2个参数method:指被代理对象的函数,被代理对象的函数通过反射被调用。所以写法固定。
 * 第3个参数args:是被代理对象函数的参数,直接加在invoke()方法的参数即可。
 * 
 * 重点是method.invoke(cc, args)前后的代码,是代理对象对被代理对象函数功能的扩展。
 * 
 * 由此可见动态代理的好处真的是很大,它解耦和了代理和被代理对象的复杂关系。具有高扩展性。
 * 
 * 看下面2个代理对象。
 * 
 * 
 */
public class CarProxy {
	public static void main(String[] args) {
		final Moveable cc = new Car();  //得到被代理的对象cc
		
		//得到代理对象proxy
		Moveable proxy = (Moveable) Proxy.newProxyInstance(cc.getClass().getClassLoader(), 
							   cc.getClass().getInterfaces(), 
							   new InvocationHandler() {
								
								@Override
								public Object invoke(Object proxy, Method method, Object[] args)
										throws Throwable {
									System.out.println("准备出发。。。");  //前后增加的内容
									method.invoke(cc, args); //proxy调用cc的函数时,由该invoke方法反射执行。
									System.out.println("车停了。。。");
									return null;
								}
							});
		proxy.run(120, "home");//代理对象调用被代理对象的方法,就会执行接口中的invoke方法,那么,增加的代理内容和被代理对象的方法都会执行。
		
		
		
		Moveable proxy2 = (Moveable) Proxy.newProxyInstance(cc.getClass().getClassLoader(), 
				   cc.getClass().getInterfaces(), 
				   new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						System.out.println("准备维修。。。");
						method.invoke(cc, args);
						System.out.println("修好了。。。");
						return null;
					}
				});
		proxy2.service();
	}
}






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值