Java动态代理

Java动态代理

大家好,又见面了,我是全栈君。

利用代理能够在执行时创建一个实现了一组给定接口的新类。

这样的功能仅仅有在编译时无法确定须要实现哪个接口时才有必要使用。

对于应用程序设计人员来说,遇到这样的情况的机会非常少。然而,对于系统程序设计人员来说。代理带来的灵活性却十分重要。

如果有一个表示接口的Class对象数组(有可能仅仅包括一个接口)。它的确切类型在编译时无法知道。

如今,想要构造一个实现这些接口的类对象,确实有些难度。如果一个Class对象表示一个实际的类,那么就能够使用newInstance方法或反射找出这个类的构造器。可是,不能实例化一个接口。而且在程序处于执行状态时也无法定义一个新类。

为了解决问题,有些程序将会生成代码。将这些代码放置在一个文件里;调用编译器;然后再载入结果类文件,Bean开发工具箱早期版本号中的BeanBox就是一个样例。

非常自然,这样做的速度会比較慢。而且须要将编译器与程序放在一起。而代理机制则是一种更好的解决方式。代理类可以在执行时创建全新的类。

这种代理类可以实现指定的接口。尤其是。它具有下列方法:

1.指定接口所须要的所有方法

2.Object类中的所有方法,比如 toString、equals等

然而,不能在执行时定义这些方法的新代码。而是要提供一个调用处理器(invocation handler)。

调用处理器是实现了InvocationHandler接口的类对象。

在这个接口中仅仅有一个方法:Object invoke(Object proxy, Method method, Object[] args)。

不管何时调用处理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用參数。调用处理器必须给出处理调用的方式。

要想创建一个代理对象,须要使用Proxy类的newProxyInstance方法。

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

这种方法有三个參数:

1.一个类载入器(class loader)。作为Java安全模型的一部分,对于系统类和从因特网上下载下来的类。能够使用不同的类载入器。

2.一个Class对象数组。每一个元素都是须要实现的接口。

3.一个调用处理器。

还有两个须要解决的问题。

怎样定义一个处理器?可以用结果代理对象做些什么?当然,这两个问题的答案取决于打算使用代理机制解决什么问题。使用代理可能出于非常多原因,比如:

1.路由对远程server的方法调用

2.在程序执行期间,将用户接口事件与动作关联起来

3.调试时跟踪方法调用

以下举个样例,在样例中使用代理和调用处理器跟踪 方法调用。而且定义了一个TraceHandler包装器类存储包装的对象。

当中的invoke方法打印出被调用方法的名字和參数。随后用包装好的对象作为隐匿參数调用这种方法。

public class TraceHandler implements InvocationHandler{
	private Object target;
	
	public TraceHandler(Object t){
		target = t;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//print method name and parameters
		//. . .
		//invoke actual method
		return method.invoke(target, args);
	}
	
	public static void main(String[] args){
		Object value = "test";
		//construct wrapper
		InvocationHandler handler = new TraceHandler(value);
		//construct proxy for all interfaces
		Class[] interfaces = value.getClass().getInterfaces();
		Object proxy = Proxy.newProxyInstance(null, interfaces, handler);
	}
	
}

以上代码main部分说明了怎样构造用于跟踪 方法调用 的代理对象。

如今,不管何时用proxy调用某个方法,这种方法的名字和參数就会打印出来,之后再用value调用它。

以下看一个完整的样例:

/**
 * This program demonstrates the use of proxies.
 */
public class ProxyTest {
	public static void main(String[] args) {
		Object[] elements = new Object[1000];

		// fill elements with proxies for the integers 1 ... 1000
		for (int i = 0; i < elements.length; i++) {
			Integer value = i + 1;
			InvocationHandler handler = new TraceHandler(value);
			Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class }, handler);
			elements[i] = proxy;
		}

		// construct a random integer
		Integer key = new Random().nextInt(elements.length) + 1;
		System.out.println("key="+key);

		// search for the key
		int result = Arrays.binarySearch(elements, key);

		// print match if found
		if (result >= 0)
			System.out.println("found:"+elements[result]);
	}
}

/**
 * An invocation handler that prints out the method name and parameters, then
 * invokes the original method
 */
class TraceHandler implements InvocationHandler {
	private Object target;
	/**
	 * Constructs a TraceHandler
	 * @param t the implicit parameter of the method call
	 */
	public TraceHandler(Object t) {
		target = t;
	}

	public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
		// print implicit argument
		System.out.print(target);
		// print method name
		System.out.print("." + m.getName() + "(");
		// print explicit arguments
		if (args != null) {
			for (int i = 0; i < args.length; i++) {
				System.out.print(args[i]);
				if (i < args.length - 1)
					System.out.print(", ");
			}
		}
		System.out.println(")");
		// invoke actual method
		return m.invoke(target, args);
	}
}

某一次执行的输出结果:

key=901
500.compareTo(901)
750.compareTo(901)
875.compareTo(901)
938.compareTo(901)
906.compareTo(901)
890.compareTo(901)
898.compareTo(901)
902.compareTo(901)
900.compareTo(901)
901.compareTo(901)
901.toString()
found:901

上例中。使用代理对象对二分查找进行跟踪。这里首先将用1-1000整数的代理填充数组,然后调用Arrays类中的binarySearch方法在数组中查找一个随机整数。最后。打印出与之匹配的元素。上述代码中,Integer类实现了Comparable接口。代理对象属于在执行时定义的类。(它有一个名字,如$Proxy0。

)这个类也实现了Comparable接口。然而,它的compareTo方法调用了代理对象处理器的invoke方法。

代理类的特性:

如今,我们已经看到了代理类的应用,接下来了解它们的一些特性。须要记住,代理类是在程序执行过程中创建的。然而,一旦被创建,就变成了常规类,与虚拟机中的不论什么其它类没有什么差别。

全部的代理类都扩展于Proxy类。一个代理类仅仅有一个实例变量——调用处理器,它定义在Proxy的超类中。为了履行代理对象的职责,所须要的不论什么附加数据都必须存储在调用处理器中。在上例中,代理Comparable对象时,TraceHandler包装了实际的对象。

全部的代理类都覆盖了Object类中的方法toString、equals和hashCode。如同全部的代理方法一样。这些方法只调用了调用处理器的invoke。Object类中的其它方法(如clone和getClass)没有被又一次定义。

未定义代理类的名字,Sun虚拟机中的Proxy类将生成一个以字符串$Proxy开头的类名。

对于特定的类载入器和预设的一组接口来说,仅仅能有一个代理类。

也就是说,假设使用同一个类载入器和接口数组调用两次newProxyInstance方法的话,仅仅可以得到同一个类的两个对象,也可以利用getProxyClass方法获得这个类:

Class proxyClass = Proxy.getProxyClass(null, interfaces);

代理类一定是public final的。假设代理类实现的全部接口都是public,那么代理类就不属于某个特定的包。否则。全部非公有的接口都必须属于同一个包,同一时候,代理类也属于这个包。

能够通过调用Proxy类中的isProxyClass方法检測一个特定的Class对象是否代表一个代理类。

基本的方法:

Java动态代理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116484.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • HashMap的hash碰撞

    HashMap的hash碰撞看了看HashMap的源码,有些心得先写下,以便以后查看,不然又要忘了,但不知道对不对,希望没误人子弟吧。主要是解释下HashMap底层实现与如何解决hash碰撞的。HashMap底层是table数组,Entry是HashMap的内部类。可以看到HashMap的key与value实际是保存在Entry中的,next是下一个Entry节点。staticfinalEntry&lt;…

  • SpringBoot(SpringMVC)文件上传下载

    SpringBoot(SpringMVC)文件上传下载话说,springboot不是一个全新的框架,它只是将其它框架整合在一起,提供一个”开箱即用”的环境。此文,利用的正是SpringMVC的功能。创建springboot项目:https://blog.csdn.net/weixin_41381863/article/details/106504682文件上传在开发中,文件上传常用的有两种方式。一、利用base64上传文件思路:客户端将要上传的文件转为base64的二进制数据,服务端利用字符串的形式接收参数,然后将base64转为相应的文件

  • vmware虚拟机重装系统_ubuntu下安装虚拟机

    vmware虚拟机重装系统_ubuntu下安装虚拟机VMware下载地址:https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html下载之后按步骤安装即可。安装完成后需要密钥,这里给出VMwareWorkstationPro16的密钥:YF390-0HF8P-M81RQ-2DXQE-M2UT6ZF71R-DMX85-08DQY-8YMNC-PPHV8密钥输入完成就可永久使用。ubuntu:首先下载Ubuntu的镜像,我下载的是ubuntu

  • 是在传统pc的路上走呢,还是跟潮流走移动互联网「建议收藏」

    这些天在准备期末考试,复习c++,可是总是对手机应用的开发感兴趣,往java方向发展可能前途更好么?以我目前接触的信息来看,貌似java语言更受欢迎,而且照现在的趋势发展,手机这种移动终端的发展应该是以后一段时间的趋势,前几天看到一个关于c++的帖子,说以后c++的程序员会两极分化,学得很好的会找到很好的工作,高新,而学的一般的,全都找不到工作。大意是这样,看了很没有信心,现在全校有一半的专业都在

  • Eurake注册中心

    Eurake注册中心 eureka找到了&nbsp;有了服务端server用于服务注册与发现,系统中其他的微服务使用客户端client链接服务端,并且维持心跳连接,server端会不断的检查client端是否存活,心跳检测,健康检查,负载均衡功能eureka.client.fetch-registry=false一个服务可以即是…

  • struts2拦截器不执行问题「建议收藏」

    struts2拦截器不执行问题「建议收藏」struts2自定义拦截器的配置博文很多,但按一些文章中配置好自定义拦截器后,设置为默认拦截器栈,拦截器方法intercept()却未被调用的问题,原因多种,以下为我问题的解决方式:配置struts2.xml加上原来其他项目中同样的配置却没出现不能被执行的问题,xml配置都差不多,可能深层问题在于action中注解的各项配置引发,有空再验证。参考:http://struts

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号