黑马程序员--Java基础--15动态代理

----------- android培训java培训java学习型技术博客、期待与您交流! ------------

动态代理

利用代理可以在运行时创建一个实现了一组给定接口的新类,这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。


JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
    JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

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

 

和动态代理有关的有两个类 
1.interface InvocationHandler 
Object invoke(Object proxy, Method method, Object[] args) 

在代理实例上处理方法调用并返回结果。

2.class Proxy 
真正表示动态代理的类,提供两个静态方法
Class<?> getProxyClass(ClassLoader loader, Class<?>[] interface) 
用来产生代理类,参数要提供interface数组,它会生成这些interface虚拟实现, 
用来冒充真实的对象。 

Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
产生代理对象,多了InvocationHandler参数(只是InvocationHandler接口的实现类), 
它与代理对象关联,当请求分发到代理对象后,会自动执行h.invoke(...)方法

 

代理类具用以下属性:

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

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

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

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

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

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

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

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

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

 

示例:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

/*
 *动态代理 
 */
public class AOPTest {
	public static void main(String[] args) {
		// 返回代理类的 java.lang.Class对象,并向其提供类加载器和接口数组
		Class clazzProxy = Proxy.getProxyClass(Collection.class
				.getClassLoader(), Collection.class);
		System.out.println(clazzProxy.getName());
		// 获取全部构造方法
		Constructor[] constructors = clazzProxy.getConstructors();
		for (Constructor constructor : constructors) {
			String name = constructor.getName();
			// StringBuffer是线程安全的,StringBuilder是线程不安全的。
			// StringBuilder在单线程的时候使用,StringBuffer则是在多线程的时候使用。
			// 因此StringBuilder比StringBuffer效率高一些。
			StringBuilder sBuilder = new StringBuilder(name);
			sBuilder.append("(");
			// 返回此 Constructor对象所表示的方法的形参类型
			Class[] clazzParams = constructor.getParameterTypes();
			for (Class clazzParam : clazzParams) {
				sBuilder.append(clazzParam.getName()).append(",");
			}
			if (clazzParams != null && clazzParams.length != 0) {
				// 删除最后的,
				sBuilder.deleteCharAt(sBuilder.length() - 1);
			}
			sBuilder.append(")");
			System.out.println(sBuilder);
		}

		// 获取全部方法名及参数
		System.out.println("--------begin methods list-------");
		Method[] methods = clazzProxy.getMethods();
		for (Method method : methods) {
			String name = method.getName();
			StringBuilder sBuilder = new StringBuilder(name);
			sBuilder.append("(");
			Class[] clazzParams = method.getParameterTypes();
			for (Class clazzParam : clazzParams) {
				sBuilder.append(clazzParam.getName()).append(",");
			}
			if (clazzParams != null && clazzParams.length != 0) {
				sBuilder.deleteCharAt(sBuilder.length() - 1);
			}
			sBuilder.append(")");
			System.out.println(sBuilder);
		}

		// 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
		Collection proxy = (Collection) Proxy.newProxyInstance(Collection.class
				.getClassLoader(), new Class[] { Collection.class },
				new InvocationHandler() {
					ArrayList target = new ArrayList();

					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						long beginTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						long endTime = System.currentTimeMillis();
						System.out.println(method.getName()
								+ " running out of " + (endTime - beginTime));
						return retVal;
					}
				});
		proxy.add("zxx");
		proxy.add("lhm");
		proxy.add("bxd");
		System.out.println(proxy.size());
	}
}

输出:

com.sun.proxy.$Proxy0

com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

--------begin methods list-------

add(java.lang.Object)

equals(java.lang.Object)

toString()

hashCode()

clear()

contains(java.lang.Object)

isEmpty()

addAll(java.util.Collection)

iterator()

size()

toArray([Ljava.lang.Object;)

toArray()

remove(java.lang.Object)

containsAll(java.util.Collection)

removeAll(java.util.Collection)

retainAll(java.util.Collection)

isProxyClass(java.lang.Class)

getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)

newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)

getInvocationHandler(java.lang.Object)

wait(long)

wait()

wait(long,int)

getClass()

notify()

notifyAll()

add running out of 317

add running out of 0

add running out of 0

size running out of 0

3


-----------  android培训 java培训 java学习型技术博客、期待与您交流! ------------


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值