黑马程序员-----动态代理Proxy

---------------------- ASP.Net+Android+IOS开发、 .Net培训、期待与您交流! ----------------------

1.动态代理
如果要为已存在的多个具有相同接口的目标类的方法增加一些系统功能,如异常处理,日志,计算方法运行时间等等,该怎样做?

这时候需要用到代理:编写一个与目标类具有相同接口的代理类,代理类的方法调用目标类的相同方法,并在调用方法上加上系统功能的代码。如图:
黑马程序员-----动态代理Proxy_第1张图片

但是有一个问题:要为系统中的各种接口的类增加代理功能,将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!
针对这一问题,JVM为我们提供了动态代理类:JVM可以在运行期动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以动态类只能用作具有相同接口的目标类的代理。

代理类的各个方法中除了调用目标的相应方法和对外返回目标返回的结果外,通常还要在方法中增加系统功能。系统功能增加的位置如下:
1)在调用目标方法之前;
2)在调用目标方法之后;
3)在调用目标方法前后;
4)在处理目标方法异常的catch块中。

动态代理的使用:当采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件配置是使用目标类,还是代理类。这样很容易切换,例如:想要日志功能时就配置代理类,否则配置目标类。这样使用很方便,而无需修改程序。

2.Proxy
Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。

static Class getProxyClass(ClassLoader loader, Class...interfaces):创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。loader指定生成动态代理类的类加载器。

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。

创建动态代理类的步骤:
有两种方法创建动态代理对象:
a. 先生成一个动态代理类,通过动态代理类来创建代理对象。
1)创建一个InvocationHandler对象。
2)使用Proxy的newProxyClass方法生成一个动态代理类。
3)获取代理类的构造器。
4)使用该构造器Constructor对象的newInstance方法创建动态代理实例。
		//创建一个InvacationHandler对象
		InvocationHandler handler = new InvocationHandler(...);
		//使用Proxy生成一个动态代理类
		Class proxyClass = Proxy.newProxyClass(Foo.class.getClassLoader(), Foo.class);
		//获取代理类构造器Constructor对象
		Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
		//调用constructor的newInstance方法创建动态实例
		Foo f = (Foo)constructor.newInstance(handler);

b.直接创建动态代理对象
1)创建一个InvocationHandler对象。
2)使用Proxy的newProxyInstance方法直接生成代理对象。
		//创建一个InvacationHandler对象
		InvocationHandler handler = new InvocationHandler(...);
		//使用Proxy直接生成一个动态代理对象
		Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader, new Class[]{Foo.class}, handler);

3.InvocationHandler
InvocationHandler是代理实例的调用处理程序实现的接口。 
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法。

Object invoke(Object proxy, Method method, Object[] args):在代理实例上处理方法调用并返回结果。
invoke方法参数如图所示:

invoke方法的返回值:一般就是handler中method.invoke(...)方法的返回值,即method.invoke返回什么,proxy的invoke就返回什么。
但如果proxy调用的方法是从Object继承而来的方法,那么除了hashCode、equals、toString方法外,都是调用自己的而不是交给handler来处理。例如:proxy.getClass()就是调用自己的getClass方法而不是交给handler处理。

下面是动态代理的原理图。
黑马程序员-----动态代理Proxy_第2张图片

下面程序将创建一个Collection的代理对象,使用被代理对象的方法并计算每个方法运行时间。
public class ProxyDemo3 {

	public static void main(String[] args) {

		//写一个Collection集合的动态代理
		//使用Proxy的newProxyInstance方法直接创建代理对象
		Collection proxyColl = (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(),
				new Class[]{Collection.class},
				new InvocationHandler(){
					//目标是ArrayList
					ArrayList target = new ArrayList();
					
					//参数:proxy代表代理对象,method代表代理对象调用的方法,args代表代理对象调用方法的实参。
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						//代理中增加的计时方法
						long startTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						long endTime = System.currentTimeMillis();
						System.out.println(method.getName() + "方法运行的时间是:" + (endTime-startTime));
						//该返回值返给代理对象调用某方法后其方法的返回值
						return retVal;
					}
				}
			);
		
		//代理对象每调用一个方法时,内部handler会调用invoke方法
		//而invoke方法会使目标target调用与代理对象调用的相同的方法。
		//它们的返回值也相同。
		proxyColl.add("hello");
		proxyColl.add("java");
		
		System.out.println(proxyColl.size());
	}

}



---------------------- ASP.Net+Android+IOS开发、 .Net培训、期待与您交流! ----------------------详细请查看: http://edu.csdn.net

你可能感兴趣的:(黑马程序员-----动态代理Proxy)