您的位置:首页 > 其它

Proxy,InvocationHandler动态代理学习笔记

2015-09-18 11:47 218 查看
动态代理的作用显而易见,比如AOP,可以在执行的你方法之前及之后做一些事情,比如事物的开启及关闭,日志的记录等等,最典型的例子莫过于Spring的AOP了...

在JDK中,Proxy 和 InvocationHandler 实现动态代理, Proxy 只要负责创建代理对象,而 InvocationHandler 用于负责调度代理对象,也就是说,你需要对目标对象的扩展错作,都应该编码在 InvocationHandler 里...

Proxy:

官网解释:

roxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

创建某一接口 Foo 的代理:

InvocationHandler handler = new MyInvocationHandler(...);

Class proxyClass = Proxy.getProxyClass(

Foo.class.getClassLoader(), new Class[] { Foo.class });

Foo f = (Foo) proxyClass.

getConstructor(new Class[] { InvocationHandler.class }).

newInstance(new Object[] { handler });



或使用以下更简单的方法:

Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

new Class[] { Foo.class },

handler);



动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

代理类具用以下属性:

代理类是公共的、最终的,而不是抽象的。

未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。

代理类扩展 java.lang.reflect.Proxy。

代理类会按同一顺序准确地实现其创建时指定的接口。

如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。

由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。

如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。

代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission。

每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。

代理实例具有以下属性:

提供代理实例 proxy 和一个由其代理类 Foo 实现的接口,以下表达式将返回 true:

proxy instanceof Foo



并且以下的强制转换操作将会成功(而不抛出 ClassCastException):

(Foo) proxy



每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态 Proxy.getInvocationHandler 方法将返回与作为其参数传递的代理实例相关的调用处理程序。

代理实例上的接口方法调用将按照该方法的文档描述进行编码,并被指派到调用处理程序的 Invoke 方法。

在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。

在多代理接口中重复的方法

当代理类的两个或多个接口包含一个具有相同名称和参数签名的方法时,代理类的接口顺序变得非常重要。在代理实例上调用重复方法 时,传递到调用处理程序的 Method 对象没有必要成为其声明类可以从接口(通过该接口调用代理方法)的引用类型指派的对象。此限制存在的原因是,生成的代理类中的相应方法实现无法确定它通过哪一个接口调用。因此,在代理实例上调用重复方法时,第一个接口中的方法的 Method 对象包含接口的代理类列表中的方法(直接或通过超级接口继承),该对象会传递到调用处理程序的 invoke 方法,无论该方法调用通过哪一种引用类型发生。

如果代理接口包含某一方法,它的名称和参数签名与 java.lang.Object 的 hashCode、equals 或 toString 方法相同,那么在代理实例上调用这样的方法时,传递到调用处理程序的 Method 对象将使 java.lang.Object 成为其声明类。换句话说,java.lang.Object 公共的非最终方法理论上在所有代理接口之前,以便确定哪一个 Method 对象传递到调用处理程序。

还要注意,当重复方法被指派到调用处理程序时,invoke 方法只可以抛出经过检查的异常类型,该异常类型可以使用所有 代理接口(可以通过它调用)中方法的 throws 子句指派一种异常类型。如果 invoke 方法抛出一个经过检查的异常,该异常没有指派给任何由一个代理接口(可以通过它调用)中的方法声明的异常类型,那么该代理实例上的调用将抛出一个未经检查的 UndeclaredThrowableException。此限制表示并非所有的由传递到 invoke 方法的 Method 对象上调用 getExceptionTypes
返回的异常类型都可以由 invoke 方法成功抛出。

接口方法:

static InvocationHandler    getInvocationHandler(Object proxy) 
          返回指定代理实例的调用处理程序。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 
          返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
static boolean  isProxyClass(Class<?> cl) 
          当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
static Object   newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。


InvocationHandler:

官网解释:

InvocationHandler 是代理实例的调用处理程序 实现的接口。

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

接口方法:

Object invoke(Object proxy,

Method method,

Object[] args)

throws Throwable

在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

参数:

proxy - 在其上调用方法的代理实例

method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

返回:

从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。

代码实例:

package com.bennytian.test;

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

/**
 * 动态代理
 * @author BennyTian
 * @date 2015年8月28日 上午11:11:20
 */
public class TestProxy {
	
	public static void main(String[] args) throws Exception {
		
		//创建一个代理对象,并把原始对象传递给 Handler
		Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new ProxyHandler(new SubjectImpl()));
		
		//调用Subject的say方法,实际上调用的会是 Handler 的invoke方法,invoke参数 proxy为proxySubject对象,methd为 say , args为 "Hello...."
		proxySubject.say("hello...");
		
		proxySubject.hello("world.");
		
	}
	
	
}

/**
 * JDK动态代理处理类
 * Proxy.newProxyInstance()负责创建代理类
 * 代理类的方法调用,由InvocationHandler.invoke来代理
 * @author BennyTian
 * @date 2015年9月18日 上午11:05:08
 */
class ProxyHandler implements InvocationHandler {
	
	//原始对象
	private Object primitiveObj;
	
	public ProxyHandler(Object primitiveObj) {
		this.primitiveObj = primitiveObj;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		System.out.println("befor:  class->: " + proxy.getClass().getName() + "   method:" + method.getName());
		
		Object result = method.invoke(this.primitiveObj, args);
		
		System.out.println("after:  class->: " + proxy.getClass().getName() + "   method:" + method.getName() +"\n\n\n");
		
		return result;
	}
	
}

/**
 * 需要代理类的接口
 * @author BennyTian
 * @date 2015年9月18日 上午11:03:38
 */
interface Subject {
	
	public void say(String hello);
	
	public String hello(String world);
}

/**
 * 许带代理的实现类
 * @author BennyTian
 * @date 2015年9月18日 上午11:03:53
 */
class SubjectImpl implements Subject {

	@Override
	public void say(String hello) {
		System.out.println("say " + hello);
	}

	@Override
	public String hello(String world) {
		System.out.println("hello " + world);
		return "hello " + world;
	}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: