当前位置: 首页 > 编程日记 > 正文

java 反射 动态代理

在上一篇文章中介绍Java注解的时候,多次提到了Java的反射API。与javax.lang.model不同的是,通过反射API可以获取程序在运行时刻的内部结构。反射API中提供的动态代理也是非常强大的功能,可以原生实现AOP中 的方法拦截功能。正如英文单词reflection的含义一样,使用反射API的时候就好像在看一个Java类在水中的倒影一样。知道了Java类的内部 结构之后,就可以与它进行交互,包括创建新的对象和调用对象中的方法等。这种交互方式与直接在源代码中使用的效果是相同的,但是又额外提供了运行时刻的灵活性。使用反射的一个最大的弊端是性能比较差。相同的操作,用反射API所需的时间大概比直接的使用要慢一两个数量级。不过现在的JVM实现中,反射操作的性能已经有了很大的提升。在灵活性与性能之间,总是需要进行权衡的。应用可以在适当的时机来使用反射API。

基本用法

Java 反射API的第一个主要作用是获取程序在运行时刻的内部结构。这对于程序的检查工具和调试器来说,是非常实用的功能。只需要短短的十几行代码,就可以遍历出来一个Java类的内部结构,包括其中的构造方法、声明的域和定义的方法等。这不得不说是一个很强大的能力。只要有了java.lang.Class类 的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。对应的方法分别是getConstructor、getField和getMethod。这三个方法还有相应的getDeclaredXXX版本,区别在于getDeclaredXXX版本的方法只会获取该类自身所声明的元素,而不会考虑继承下来的。Constructor、Field和Method这三个类分别表示类中的构造方法、域和方法。这些类中的方法可以获取到所对应结构的元数据。

反射API的另外一个作用是在运行时刻对一个Java对象进行操作。 这些操作包括动态创建一个Java类的对象,获取某个域的值以及调用某个方法。在Java源代码中编写的对类和对象的操作,都可以在运行时刻通过反射API来实现。考虑下面一个简单的Java类。

1 class MyClass {
2     public int count;
3     public MyClass(int start) {
4         count = start;
5     }
6     public void increase(int step) {
7         count = count + step;
8     }
9 } 

使用一般做法和反射API都非常简单。

 1 MyClass myClass = new MyClass(0); //一般做法
 2 myClass.increase(2);
 3 System.out.println("Normal -> " + myClass.count);
 4 try {
 5     Constructor constructor = MyClass.class.getConstructor(int.class); //获取构造方法
 6     MyClass myClassReflect = constructor.newInstance(10); //创建对象
 7     Method method = MyClass.class.getMethod("increase", int.class);  //获取方法
 8     method.invoke(myClassReflect, 5); //调用方法
 9     Field field = MyClass.class.getField("count"); //获取域
10     System.out.println("Reflect -> " + field.getInt(myClassReflect)); //获取域的值
11 } catch (Exception e) { 
12     e.printStackTrace();
13 } 

由于数组的特殊性,Array类提供了一系列的静态方法用来创建数组和对数组中的元素进行访问和操作。

1 Object array = Array.newInstance(String.class, 10); //等价于 new String[10]
2 Array.set(array, 0, "Hello");  //等价于array[0] = "Hello"
3 Array.set(array, 1, "World");  //等价于array[1] = "World"
4 System.out.println(Array.get(array, 0));  //等价于array[0]

使用Java反射API的时候可以绕过Java默认的访问控制检查,比如可以直接获取到对象的私有域的值或是调用私有方法。只需要在获取到Constructor、Field和Method类的对象之后,调用setAccessible方法并设为true即可。有了这种机制,就可以很方便的在运行时刻获取到程序的内部状态。

处理泛型

Java 5中引入了泛型的概念之后,Java反射API也做了相应的修改,以提供对泛型的支持。由于类型擦除机制的存在,泛型类中的类型参数等信息,在运行时刻是不存在的。JVM看到的都是原始类型。对此,Java 5对Java类文件的格式做了修订,添加了Signature属性,用来包含不在JVM类型系统中的类型信息。比如以java.util.List接口为例,在其类文件中的Signature属性的声明是<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;; ,这就说明List接口有一个类型参数E。在运行时刻,JVM会读取Signature属性的内容并提供给反射API来使用。

比如在代码中声明了一个域是List<String>类型的,虽然在运行时刻其类型会变成原始类型List,但是仍然可以通过反射来获取到所用的实际的类型参数。

 1 Field field = Pair.class.getDeclaredField("myList"); //myList的类型是List 
 2 Type type = field.getGenericType(); 
 3 if (type instanceof ParameterizedType) {     
 4     ParameterizedType paramType = (ParameterizedType) type;     
 5     Type[] actualTypes = paramType.getActualTypeArguments();     
 6     for (Type aType : actualTypes) {         
 7         if (aType instanceof Class) {         
 8             Class clz = (Class) aType;             
 9             System.out.println(clz.getName()); //输出java.lang.String         
10         }     
11     } 
12 }  

动态代理

熟悉设计模式的人对于代理模式可 能都不陌生。 代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互。代理的存在对于调用者来说是透明的,调用者看到的只是接口。代理对象则可以封装一些内部的处理逻辑,如访问控制、远程通信、日志、缓存等。比如一个对象访问代理就可以在普通的访问机制之上添加缓存的支持。这种模式在RMI和EJB中都得到了广泛的使用。传统的代理模式的实现,需要在源代码中添加一些附加的类。这些类一般是手写或是通过工具来自动生成。JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。熟悉AOP的人对这种使用模式应该不陌生。但是这种方式不需要依赖AspectJ等AOP框架。

下面的代码用来代理一个实现了List接口的对象。所实现的功能也非常简单,那就是禁止使用List接口中的add方法。如果在getList中传入一个实现List接口的对象,那么返回的实际就是一个代理对象,尝试在该对象上调用add方法就会抛出来异常。

 1 public List getList(final List list) {
 2     return (List) Proxy.newProxyInstance(DummyProxy.class.getClassLoader(), new Class[] { List.class },
 3         new InvocationHandler() {
 4             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 5                 if ("add".equals(method.getName())) {
 6                     throw new UnsupportedOperationException();
 7                 }
 8                 else {
 9                     return method.invoke(list, args);
10                 }
11             }
12         });
13  } 

这里的实际流程是,当代理对象的add方法被调用的时候,InvocationHandler中的invoke方法会被调用。参数method就包含了调用的基本信息。因为方法名称是add,所以会抛出相关的异常。如果调用的是其它方法的话,则执行原来的逻辑。

使用案例

Java 反射API的存在,为Java语言添加了一定程度上的动态性,可以实现某些动态语言中的功能。比如在JavaScript的代码中,可以通过 obj["set" + propName]()来根据变量propName的值找到对应的方法进行调用。虽然在Java源代码中不能这么写,但是通过反射API同样可以实现类似 的功能。这对于处理某些遗留代码来说是有帮助的。比如所需要使用的类有多个版本,每个版本所提供的方法名称和参数不尽相同。而调用代码又必须与这些不同的版本都能协同工作,就可以通过反射API来依次检查实际的类中是否包含某个方法来选择性的调用。

Java 反射API实际上定义了一种相对于编译时刻而言更加松散的契约。如果被调用的Java对象中并不包含某个方法,而在调用者代码中进行引用的话,在编译时刻就会出现错误。而反射API则可以把这样的检查推迟到运行时刻来完成。通过把Java中的字节代码增强、类加载器和反射API结合起来,可以处理一些对灵 活性要求很高的场景。

在 有些情况下,可能会需要从远端加载一个Java类来执行。比如一个客户端Java程序可以通过网络从服务器端下载Java类来执行,从而可以实现自动更新 的机制。当代码逻辑需要更新的时候,只需要部署一个新的Java类到服务器端即可。一般的做法是通过自定义类加载器下载了类字节代码之后,定义出 Class类的对象,再通过newInstance方法就可以创建出实例了。不过这种做法要求客户端和服务器端都具有某个接口的定义,从服务器端下载的是 这个接口的实现。这样的话才能在客户端进行所需的类型转换,并通过接口来使用这个对象实例。如果希望客户端和服务器端采用更加松散的契约的话,使用反射API就可以了。两者之间的契约只需要在方法的名称和参数这个级别就足够了。服务器端Java类并不需要实现特定的接口,可以是一般的Java类。

动态代理的使用场景就更加广泛了。需要使用AOP中的方法拦截功能的地方都可以用到动态代理。Spring框架的AOP实现默认也使用动态代理。不过JDK中的动态代理只支持对接口的代理,不能对一个普通的Java类提供代理。不过这种实现在大部分的时候已经够用了。

参考资料

  • Classworking toolkit: Reflecting  generics
  • D?ecorating with dynamic proxies

转载于:https://www.cnblogs.com/zoucaitou/p/4161622.html

相关文章:

公路病害检测有了“智慧眼”,思谋AI“助力”广东省高速公路

近日&#xff0c;思谋科技与广东省某高速企业达成合作&#xff0c;智慧交通一体化平台病害检测模块已成熟落地&#xff0c;将使广东省高速公路病害检测进入高频率、高效率、智能化时代&#xff0c;以行业领先的AI技术助力智慧交通产业的发展。 近年来&#xff0c;我国公路建设…

无准备,不编程——计算机达人成长之路(15)连载

8、俄罗斯方块&#xff08;三&#xff09;编码 嬉闹归嬉闹&#xff0c;最终二人静下心来&#xff0c;绍绍开始请教俄罗斯方块的设计&#xff0c;木鸿飞也不藏拙&#xff0c;马上开始讲解&#xff1b;“游戏其实分为三重循环&#xff0c;也就是可以分为三个部分。” “哪三个&am…

Memcached 缓存系统的-介绍、安装以及应用

一. memcached 是什么?memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.memcached是一个高性能的、分布式内存对象缓存系统&am…

被算法“监控”的打工人,这家公司 150 人被算法裁定为“不敬业”

整理 | 禾木木 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 近日&#xff0c;一家公司通过 AI 算法裁掉三成员工的消息在网上引起热议。当你上班时突然收到一封邮件显示“效率低下”而要求马上离开公司&#xff0c;你会怎么想呢&#xff1f; 据 Game World Obser…

(转)I 帧和 IDR 帧的区别

I 帧和 IDR 帧的区别&#xff1a;http://blog.csdn.net/skygray/article/details/6223358 IDR 帧属于 I 帧。解码器收到 IDR frame 时&#xff0c;将所有的参考帧队列丢弃 &#xff08;用x264_reference_reset 函数实现——在 encoder.c 文件中&#xff09; 。这点是所有 I 帧…

ExtJs 备忘录(3)—— Form表单(三) [ 数据验证 ]

正文一、资料 1.1. 表单提示的方式设置&#xff0c;如&#xff1a; Ext.form.Field.prototype.msgTargetside 该设置为枚举值&#xff1a;qtip,side,title,under qtip&#xff1a;把鼠标移动到控件就可以显示提示信息了&#xff0c;有点像设置了title的input标签一样的效果…

Dynamo 以及一致性哈希简介

本介绍参考 Amazon 的 Dynamo 论文。需要更详细更准确信息的同学请直接阅读原文。 (原文地址http://s3.amazonaws.com/AllThingsDistributed/sosp/amazon-dynamo-sosp2007.pdf) 这篇论文本身没提出什么新的思想&#xff0c;正如论文中所说&#xff0c;贡献在于把非常多的技术结…

如何更快速加载你的JS页面

确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具&#xff0c;只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 尽量减少DOM访问 使用JavaScript访问DOM元…

性价比超高:苹果发布了新数据集,助力室内场景理解

作者&#xff1a;刘媛媛 来源&#xff1a;数据实战派前言计算机视觉界一直渴望能够找到一种方法&#xff0c;让计算机和人们都能够理解室内场景的复杂性。对于许多基本的场景理解任务&#xff0c;很难或不可能从真实图像中获得每像素地面实况标签。一部分研究人员选择通过使用交…

42. fastjson处理下划线和驼峰问题的方法和源码分析

一. 前言 在开发过程中经常遇到json解析和生成的问题&#xff0c;所以用自己也一直用fastjson来实现这个功能。 但是&#xff0c;最近遇到一个问题: json字符串里面的数据很多都是"_"下划线的比如&#xff0c;op_id。 而在java里面&#xff0c;很多都是驼峰的写法&…

Cacti Weathermap添加主机在线状态图示检测

Weathermap 中文名称翻译为 气象图weathermap版本支持&#xff1a;0.94及最新版1、首先添加气象图 图例像这样。编辑需要添加图例的气象图配置文件 (气象图配置文件默认在/var/www/html/plugins/weathermap/configs/ 目录中)其中 KEYPOS指定全局图例名称以及 图例位置和 图例标…

jquery技巧总结

一、简介1.1、概述随着WEB2.0及ajax思想在互联网上的快速发展传播&#xff0c;陆续出现了一些优秀的Js框架&#xff0c;其中比较著名的有Prototype、YUI、jQuery、mootools、Bindows以及国内的JSVM框架等&#xff0c;通过将这些JS框架应用到我们的项目中能够使程序员从设计和书…

Unet网络实现叶子病虫害图像分割

作者|李秋键 出品|AI科技大本营(ID:rgznai100) 智能化农业作为人工智能应用的重要领域&#xff0c;对较高的图像处理能力要求较高&#xff0c;其中图像分割作为图像处理方法在其中起着重要作用。图像分割是图像分析的关键步骤, 在复杂的自然背景下进行图像分割, 难度较大。 在传…

SilverLight 4页面跳转大全

http://blog.csdn.net/lihaiyin/article/details/5674766 //Silverlight页面的跳转 //(Application.Current.RootVisual as IContent).Content new DragControl(); //普通页面跳转 HtmlWindow html HtmlPage.Window; //html.Navigate(new Uri("http://www.0379zd.co…

Linux 性能监控常用命令

工具简单介绍top查看进程活动状态以及一些系统状况vmstat查看系统状态、硬件和系统信息等iostat查看CPU 负载&#xff0c;硬盘状况sar综合工具&#xff0c;查看系统状况mpstat查看多处理器状况netstat查看网络状况iptraf实时网络状况监测tcpdump抓取网络数据包&#xff0c;详细…

jquery 1.6以上版本 全选

2019独角兽企业重金招聘Python工程师标准>>> <html xmlns"http://www.w3.org/1999/xhtml"> <head><title>全选&#xff0c;不全选&#xff0c;反选</title><script src"jquery.js" type"text/javascript"…

想提高代码水平,做到这点就够了

【CSDN 编者按】本篇文章来自于《程序员》杂志的一位老读者&#xff0c;他从大学时最深刻的记忆谈起&#xff0c;到编程路上的心得体会&#xff0c;再到对广大年轻程序员的建议&#xff0c;全篇娓娓道来&#xff0c;希望能对大家有所帮助。作者 | 姜宁 华为开源能力中心技术专…

modified: xxx(modified content, untracked content)

from:://http://blog.csdn.net/huguohu2006/article/details/7045052 提交代码到服务器后发现git clone下来的有些目录是空的。 查看服务器的目录果然是空的。看本季git add . 后查看git status modified: xxx(modified content, untracked content) 大概意思是xxx目录没有…

一些JSON相关的函数

JSON作为一种轻量的数据传输格式&#xff0c;越来越受到人们的青睐。下面是我仿照Prototype的一些实现。 JSONFilter:/^///*-secure-([/s/S]*)/*///s*$/, unfilterJSON:function(json,filter) { return json.replace((filter || dom.JSONFilter), function(a,b){ return b |…

太任性!17 岁少年买不到回国机票,因“泄愤”攻击航司系统,被判刑 4 年

整理 | 王晓曼出品 | 程序人生&#xff08;ID&#xff1a;coder _life&#xff09;因买不到回国机票&#xff0c;一气之下&#xff0c;身处境外的17岁少年陈某竟多次、持续攻击某航空公司计算机系统&#xff0c;致使该航空公司对外服务网络全面瘫痪近四小时&#xff0c;5000余万…

hbase2.0.0-安装部署

依赖hadoop 环境&#xff0c;我这边的版本是hadoop-2.6.5 选择hbase2.0.0版本的时候&#xff0c;去官网查看支持的hadoop版本 1、伪分布式安装 下载&#xff1a;http://mirror.bit.edu.cn/apache/hbase/2.0.0-alpha4/ 版本&#xff1a;hbase-2.0.0-alpha4-bin.tar.gz 2、tar -z…

Git详解之二 Git基础

Git 基础 读完本章你就能上手使用 Git 了。本章将介绍几个最基本的&#xff0c;也是最常用的 Git 命令&#xff0c;以后绝大多数时间里用到的也就是这几个命令。读完本章&#xff0c;你就能初始化一个新的代码仓库&#xff0c;做一些适当配置&#xff1b;开始或停止跟踪某些文件…

一致性哈希(Consistent Hashing)

在大型web应用中&#xff0c;缓存可算是当今的一个标准开发配置了。在大规模的缓存应用中&#xff0c;应运而生了分布式缓存系统。分布式缓存系统的基本原理&#xff0c;大家也有所耳闻。key-value如何均匀的分散到集群中&#xff1f;说到此&#xff0c;最常规的方式莫过于hash…

CT片居然可以这么玩:用头部CT断层扫描片复原三维头像

作者&#xff1a;天元浪子 来源&#xff1a;Python作业辅导员前言CT是现代医学影像的主力设备&#xff0c;寻常百姓并不陌生。通常&#xff0c;一张CT片由多张连续断层扫描的图像组成。在医生眼中&#xff0c;CT片展示了人体器官的形态和性质&#xff0c;是判断病人健康状况的重…

Nginx学习笔记(一) Nginx架构

Nginx全程是什么&#xff1f; Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。 daemon守护线程 nginx在启动后&#xff0c;在unix系统中会以daemon的方式在后台运行&#xff0c;后台进程包含一个master…

PXE实现批量部署linux系统

pxe批量部署linux服务器1、pxe介绍PXE是有intel设计的协议&#xff0c;它可以使计算机通过网络启动&#xff0c;协议分为client和server两端&#xff0c;PXEclient在网卡的ROM中&#xff0c;当计算机引导时&#xff0c;BIOS把PXE client调入内存中执行&#xff0c;并显示出命令…

首场见习挑战赛倒计时3天!20000元奖学金瓜分就等你了!

CSDN软件开发精英赛是基于“C认证—软件工程师能力认证考试”而设立的编程比赛&#xff0c;大赛联合广大科技企业设置丰厚礼品及30万元奖学金。从7月22日官宣到今日&#xff0c;短短一个月内&#xff0c;大赛已经吸引了来自全国的2000&#xff0b;开发者参与其中。第一轮“见习…

一致性哈希算法以及其PHP实现

在做服务器负载均衡时候可供选择的负载均衡的算法有很多&#xff0c;包括&#xff1a; 轮循算法&#xff08;Round Robin&#xff09;、哈希算法&#xff08;HASH&#xff09;、最少连接算法&#xff08;Least Connection&#xff09;、响应速度算法&#xff08;Response Time…

Linux入门(四)

目录&#xff1a; 1234567891011121314一、根文件系统层级标准FHS二、bash的基础特性&#xff08;一&#xff09;1.命令历史 2.命令行补全 3.路径补全 4.命令行展开 5.命令执行的状态结果 6.引用 7.快捷键 三、目录管理相关命令mkdir、rmdir、tree四、引用命令的执行结果五、文…

OSI[七层]与TCP/IP[四层]模型简述简图

OSI参考模型&#xff08;OSI/RM&#xff09;的全称是开放系统互连参考模型&#xff08;Open System Interconnection Reference Model&#xff0c;OSI/RM&#xff09;&#xff0c;它是由国际标准化组织&#xff08;International Standard Organization&#xff0c;ISO&#xf…