本文共 5117 字,大约阅读时间需要 17 分钟。
谈到Spring时大家都会想到它的核心原理是IOC/DI,其实Spring实现IOC/DI的背后依靠的是Java反射机制。不仅Spring使用反射机制,Hibernate的ORM框架也是使用反射机制实现的,AOP动态代理也是大量使用反射实现的,所以Java反射机制其实已经被大量应用在我们的程序中,只是平时我们做业务应用开发时直接使用Java反射的机会比较少,因为我们不需要重复发明轮子,大部分应用的开发都会使用一些像Spring这样成熟的框架,开发人员只需要提供框架配置文件即可。框架让我们可以更专注业务逻辑开发,但对于提高架构设计能力,我们还是需要掌握框架背后的实现,说不定将来我们需要开发自己的框架或者定制开源框架。
Reflection,这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
反射被大量使用肯定有其优点,从上面的定义可以看出反射的优点就是动态灵活,一切都是在运行时期根据具体情况(配置)决定创建哪种对象、调用哪个方法等。这种灵活性可以方便我们设计解耦,这也是Spring做得最好的事情之一,所以我们可以很方便的替换某些class文件而不需要重新编译相关类。事物都是两面的,反射当然也有缺点,对于编译期能确定的事情,编译器可以或多或少做些优化,而运行期就像解释执行一样,编译器一般没法进行优化,所以使用反射性能要打点折扣;另外反射破坏类的封装性,通过反射我们可以访问类的任何成员,包括private成员,这违反了面向对象设计原则。
java.lang.Class类:反射中最常出现的类,它跟其它JAVA类没什么两样只是名字恰巧叫Class,都是继承于Object类,其实它就是JAVA类的抽象,用来描述类的元数据,比如每个类都有类名、装载器、哈希等,这些都是Class类的属性。Class没有public的构造函数,当Java虚拟机载入一个类的时候,它就会自动创建一个Class类的实例来表示这个类,比如java.lang.String,我们可以通过String.class来获取对应的Class类实例;或者我们也可以通过Class.forName("类全名")来获取一个Class对象。
反射常用的一些方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Class c = Class.forName( "className" ); //className必须为全名 Object obj = c.newInstance(); //创建实例 Constructor getConstructor(Class[] params); //根据指定参数获得public构造函数 Constructor[] getConstructors(); //获得所有public构造函数 Method getMethod(String name, Class[] params); //根据方法名,参数类型获得public方法 Method[] getMethods(); //获得所有的public方法 Field getField(String name); //根据变量名得到相应的public变量 Field[] getFields(); //获得类中所以public的变量 |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | package com.stevex.app.forkjoin; import java.math.BigDecimal; public class Account implements java.io.Serializable{ public static final long serialVersionUID = -6928173218630110362L; private String id; private String clientId; private BigDecimal balance; public Account(){ //default constructor } public Account(String id, String clientId) { this .id = id; this .clientId = clientId; balance = BigDecimal.ZERO; } public synchronized void withdraw(String clientId, BigDecimal amount){ validateClient(clientId); if (amount.compareTo(balance) < 1 ){ balance.subtract(amount); } else { throw new RuntimeException( "余额不足" ); } } private void validateClient(String clientId) { if (! this .clientId.equals(clientId)){ throw new RuntimeException( "非法客户" ); } } public synchronized void deposit(BigDecimal amount){ if (amount.compareTo(BigDecimal.ZERO) == 1 ){ balance.add(amount); } else { throw new RuntimeException( "不允许透支" ); } } public String getClientId() { return clientId; } public void setClientId(String clientId) { this .clientId = clientId; } public String getId() { return id; } public void setId(String id) { this .id = id; } } package com.stevex.app.forkjoin; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class c = Class.forName( "com.stevex.app.forkjoin.Account" ); Account account = (Account) c.newInstance(); Constructor[] cons = c.getConstructors(); Method[] methods = c.getMethods(); Field[] fields = c.getFields(); for (Constructor con : cons){ System.out.println(con); } for (Method m : methods){ System.out.println(m); } for (Field f : fields){ System.out.println(f); } } } |
发现输出结果蛮有趣的,就连父类Object中定义的public成员都输出了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public com.stevex.app.forkjoin.Account() public com.stevex.app.forkjoin.Account(java.lang. String ,java.lang. String ) public java.lang. String com.stevex.app.forkjoin.Account.getId() public synchronized void com.stevex.app.forkjoin.Account.withdraw(java.lang. String ,java.math.BigDecimal) public synchronized void com.stevex.app.forkjoin.Account.deposit(java.math.BigDecimal) public java.lang. String com.stevex.app.forkjoin.Account.getClientId() public void com.stevex.app.forkjoin.Account.setClientId(java.lang. String ) public void com.stevex.app.forkjoin.Account.setId(java.lang. String ) public final void java.lang. Object .wait() throws java.lang.InterruptedException public final void java.lang. Object .wait(long, int ) throws java.lang.InterruptedException public final native void java.lang. Object .wait(long) throws java.lang.InterruptedException public boolean java.lang. Object .equals(java.lang. Object ) public java.lang. String java.lang. Object .toString() public native int java.lang. Object .hashCode() public final native java.lang.Class java.lang. Object .getClass() public final native void java.lang. Object .notify() public final native void java.lang. Object .notifyAll() public static final long com.stevex.app.forkjoin.Account.serialVersionUID |