`
zhrb
  • 浏览: 1706 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

动态代理(proxy)类和类加载器

阅读更多
动态代理(proxy)类和类加载器
作者:zhrb
学习该技术之前,需要对反射Method对象的使用有一定的了解。
可以在运行时创建一个实现了一组给定接口的新类(代理类)。不过该代理类和Hibernate中使用的代理类使用的是不一样的技术。
该代理类具有下列方法:
 指定接口所需要的全部方法
 Object类中的全部方法
但是该代理类不能定义其所实现接口的方法,即,不能编写实现该方法的代码。所以需要invocation handler,该invocation handler实现了InvocationHandler接口的类对象,该接口中方法的定义:
 Object
invoke(Object proxy, Method method, Object[] args) 
          在代理实例上处理方法调用并返回结果。

          在代理实例上处理方法调用并返回结果。
那么当我们调用新生成的代理对象的方法时,invocation handler的invoke方法就会被调用。从而间接达到了为代理类增加自己代码的目的。
以下面这个例子为例,我们希望实现Comparable接口的对象的compareTo方法被调用时执行一些额外的代码,如打印被调用的方法(compareTo)名称、调用该方法传递进来的参数。我们可以使用代理类实现。具体代码详见《java核心技术 卷1:基础知识》接口与内部类这章的代理小节中的例6-7, ProxyTest.java代码。
为了做实验我们编写自己的代码进行操作,有如下接口与类:
public interface ShapeInterface {
	public double getArea();
	
}


Cricle、Square、Triangle类实现了ShapeInterface接口。
我们希望实现如下功能,当ShapeInterface的实现类的getArea()方法被调用的时候,打印出被代理对象的类型、该代理对象中被调用的方法名、参数个数、参数类型、参数值,如果没有参数传递进来则打印“该方法没有参数”,最后执行getArea()方法本身。这些功能可以算是为代理类新增的代码,但我们不能直接在代理类中编写代码,我们需要为ShapeInterface接口定义一个invocation handler,即下面的ShapeProxyHandler,见如下代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ShapeProxyHandler implements InvocationHandler {
	public ShapeProxyHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.print("你所要代理的对象类型是"+target.getClass().getName());
		System.out.println(",要执行"+method.getName()+"方法");
		if (args!=null){
			System.out.print("该方法有"+args.length+"个参数,分别是");
			for(Object e:args){
				System.out.print(e.getClass().getName());
				System.out.print(","+e);
			}
		}
		else{
			System.out.println("该方法没有参数");
		}

		
		return method.invoke(target, args);
	}
	
	private Object target;//存放要代理的对象
}


该invocation handler编写好以后,编写如下测试代码进行测试:
import java.lang.reflect.Proxy;
import proxy.ShapeInterface;

public class ProxyTestShape {
	public static void main(String[] args) {
		Object[] elements = new Object[3];//用于存放代理对象的数组
	
   	  	ShapeProxyHandler proxyHandler1 = new ShapeProxyHandler(new Circle());
   	  	Object proxy1 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1);
	    elements[0] = proxy1;
	    
	    ShapeProxyHandler proxyHandler2 = new ShapeProxyHandler(new Square(10));
   	  	Object proxy2 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler2);
	    elements[1] = proxy2;
	    
	    ShapeProxyHandler proxyHandler3 = new ShapeProxyHandler(new Triangle(10, 20, 30));
   	  	Object proxy3 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler3);
	    elements[2] = proxy3;
	    
	    invokeGetArea(elements);
	}
	
	public static void invokeGetArea(Object[] elements){
		
		for(Object e:elements){
			System.out.println("返回结果是="+((ShapeInterface)e).getArea());
			System.out.println("===============END=============");
		}
	}
}



其中下面这句代码:
Object proxy1 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1);


使用了Proxy类的newProxyInstance方法。
我们先来看一下该方法第一个参数:
类加载器(class loader),ShapeInterface.class.getClassLoader()就是ShapeInterface这个类型的类加载器。该处代码和java核心技术的参考代码又不一样,核心技术里面该处的参数为null,主要是因为核心技术里面实现的是Comparable接口,而该接口是被Extension类加载器。Extension类加载器用于从jre/lib/ext目录加载,而Comparable作为java.lang包中的接口正好位于jre/lib/ext目录。但我们自定义的接口ShapeInterface却不能被Extension类加载器加载而是被System类加载器加载,所以此处的代码不能像java核心技术中的那样设置为null。每个线程都有一个对类加载器的引用,称为上下文类加载器。主线程的上下文类加载器是System类加载器。在这里main方法所在的主线程的就使用了System类加载器。也就是说主线程的上下文类加载器和自定义接口的ShapeInterface类加载器刚好一样,都是System类加载器。那么上面这段代码可以改成如下这段代码:
Object proxy1 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1);

其中Thread.currentThread().getContextClassLoader()这句代码就是获得当前线程(主线程)的上下文类加载器。
我们再来看一下该方法第二个参数:
    这个参数new Class[] { ShapeInterface.class }是一个数组,意味着我们实际上生成的代理类可以实现多个接口。
该方法的第三个参数:
    proxyHandler1就是我们实现的invocation handler,也就是在这个invocation handler中我们编写了想要为代理类增加的代码。
    使用代理类还应注意,我们只能生成接口的代理类而不能生成某个类的代理类。
代理类的其他案例:
    实际开发中可能会遇到新旧版本的匹配问题,如旧版本接口的和新版本的接口不一样,但是希望新旧版本都可使用,也可以使用动态代理类实现。具体请参考附录的参考资料2.
    代理技术为Java提供了极大的动态性可以使我们的编程更加灵活,所以有兴趣的话大家可以了解一下动态代理类。

参考代码见附件

参考资料:
1. JAVA核心技术 卷1:基础知识--接口与内部类--代理小节
2. JAVA核心技术 卷2:高级特性--安全--类加载器小节
3.深入理解Java 7:核心技术与最佳实践 成富 著
分享到:
评论

相关推荐

    foxyproxy_standard

    FoxyProxy 是一款高级代理服务器管理工具,是 Firefox 火狐浏览器的代理插件,相比比 SwitchProxy、ProxyButton、QuickProxy、xyzproxy、ProxyTex 等扩展提供更多的功能。 FoxyProxy 通过使用通配符、正则表达式和...

    java设计模式【之】JDK动态代理【源码】【场景:帮爸爸买菜】.rar

    * 动态代理中的静态方法:java.lang.reflect.Proxy.newProxyInstance (ClassLoader(类加载器),interface(接口),handler(监听处理器)) * * 代码示例:《帮爸爸买菜》 * 1.Father: 被代理类,必须需要实现接口 ...

    proxy_manager:代理经理

    代理经理代理管理器,用于从Internet下载具有所需参数(协议和匿名程度)的代理服务器列表,并与加载服务器一起工作(例如,从该列表中获取随机代理服务器)。安装从PyPi安装软件包$ pip3 install proxy-manager-g4...

    react-proxy, 代理在不卸载或者丢失它的状态的情况下响应组件.zip

    react-proxy, 代理在不卸载或者丢失它的状态的情况下响应组件 代理 一种通用反应元件代理,通过热加载器作为新引擎。 1.x-和 2.x你正在查看 1.x 分支中广泛使用的自述文件。 然而,我们打算逐步过渡诸如 react-...

    疯狂JAVA讲义

    6.8.3 枚举类的属性、方法和构造器 220 6.8.4 实现接口的枚举类 223 6.8.5 包含抽象方法的枚举类 224 6.9 对象与垃圾回收 225 6.9.1 对象在内存中的状态 226 6.9.2 强制垃圾回收 227 6.9.3 finalize方法 228 ...

    Java 基础核心总结 +经典算法大全.rar

    《Java 基础核心总结》 Java 概述 什么是 Java2 Java 的特点Java ...JDK Proxy 和 CGLIB 的对比动态代理的实际应用 Spring AOP 变量 变量汇总实例变量 实例变量的特点全局变量 静态变量 静态变量的特点类变量 局部变量

    proxy_manager:Ruby 代理管理器。 用于在 parserweb 机器人中轻松使用代理的 Gem

    安装将此行添加到应用程序的 Gemfile 中: gem 'proxy_manager' 然后执行: $ bundle 或者自己安装: $ gem install proxy_manager用法加载代理列表从阵列 (IP:PORT) proxy = ProxyManager :: Proxy . new ( [ '...

    react-proxy-loader:将react组件包装在代理组件中以启用代码拆分

    React代理加载器 将React组件包装在代理组件中以启用代码拆分,这将按需加载React组件及其依赖项。 要求 此模块至少需要Node v6.9.0和Webpack v4.0.0。 入门 首先,您需要安装react-proxy-loader : $ npm install ...

    php中通过虚代理实现延迟加载的实现代码

    不过我实现的这个版本有局限性: 只适用于对象,无法代理数组等基本数据类型(需要用 ArrayObject 一类的内置对象封装) 被代理之后,一些带有操作符重载性质的接口实现就失效了,例如 ArrayAccess 的索引器、...

    spring源码详解

    一、什么是AOP 二、AOP相关概念 (1)切面 (Aspect) 交叉业务,也就是通用的业务逻辑,比如日志、事务。 (2)通知(Advice) ... 2、类加载器织入 3、动态代理织入 AspectJ:1,2 Spring AOP:3

    docker-browsermob-proxy:Browsermob-Proxy 的 Dockerfile

    Docker 中的 BrowserMob 代理 ... 默认情况下,它会在端口 9090 上加载 BrowserMob-Proxy,并在端口 9091 上启动代理侦听器。 用法 $ docker run -p 9090:9090 -p 9091:9091 shopigniter/browsermob-proxy

    免费超全面的Java基础类型,容器,并发,IO流,面向对象,Web编程等代码总结

    Proxy动态代理机制详解 从整体上观察对象 网络开发 Servlet基础,生命周期执行过程 Http请求详解,握手挥手流程简介 会话跟踪技术,Session和Cookie详解 过滤器、监听器、拦截器,应用详解 Servlet 集成 C3P0

    platform-independent Proxy Grabber v1.5:应用程序允许从 Proxybase 加载代理-开源

    : • 设计简单 • 易于使用 • 处理速度快 • 与平台无关 v1.5 中的新功能: • 错误修正(代理可以再次加载) • 显示自己的 ip、提供者和位置 说明: 1. 启动应用程序,输入密码“1234”,不带引号,然后回车确认...

    代理助手-crx插件

    语言:English,русский,中文 (简体) ...保存后重新加载代理自动设置 3.添加默认的中国旁路列表 4.完全重写UI 5.添加中文支持 2012-12-7(1.1.4) 1.支持直接模式,连接永远不会使用代理。 2.更新固定UI和错误。

    dns-proxy:使用节点构建的简单DNS代理,该节点具有按用户域阻止功能

    设置安装节点克隆回购安装 API 和 Core 模块的依赖项运行主编辑过滤器将 DNS 流量重定向到运行此项目的设备自动重新加载默认情况下,将自动考虑 source.d 和 filters.d 中的所有更改。 您不必停止 DNSProxy添加更多...

    node-proxy-injector:一种将第三方css和js注入到代理服务器响应中的工具

    节点代理注入器该脚本允许您代理远程服务器,并将css样式表和js脚本注入到远程服务器响应中。 该工具支持实时重新加载,但是您必须安装和配置浏览器扩展。 有关详细信息,请访问 。 请注意,该工具正在开发中!用法$...

    内核驱动加载与调试1

    内核驱动加载与调试符号服务器 _NT_SYMBOL_PATH代理服务器 _NT_SYMBOL_PROXY驱动的加载安装一个驱动等同于安装一个服务打开服务管理器成

    packet-logger:用于tera-proxy的GUI数据包记录器mod

    保存和加载过滤器和数据包日志 定义参考 在选择数据包时单击定义按钮将打开用于解析该数据包的定义。 此窗口是可拖动和可调整大小的(从右下角调整大小) 六角工具 该工具将是一种快速计算工具,用于从数据包数据...

    Spring.html

    代理Proxy 底层实现 JDK动态代理(默认) 基于接口:代理对象与目标对象是兄弟关系,目标类必须实现接口 CGLIB动态代理 基于父类:代理对象与目标对象是父子关系.目标不能被final修饰 修改默认代理方法:...

    ProxyManager::top_hat::sparkles::rainbow:OOP代理包装器实用程序-生成和管理对象的代理

    ocramius / proxy-manager的维护者以及成千上万的其他软件包正在与Tidelift一起使用,以为您用于构建应用程序的开源依赖项提供商业支持和维护。 节省时间,降低风险并改善代码运行状况,同时向维护人员支付所使用的...

Global site tag (gtag.js) - Google Analytics