学习Spring框架的技术内幕引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必
学习Spring框架的技术内幕
引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”。以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助。):
[1] Java反射知识-->Spring IoC
[2] 属性编辑器,即PropertyEditor-->Spring IoC
[3] Java动态代理-->Spring AOP
[4] 线程本地变量,即ThreadLocal-->Spring事务管理
??? Java语言允许通过程序化的方式间接对Class进行操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。
简单实例
??? 我们将从一个简单例子开始探访Java反射机制的征程,下面的Car类拥有两个构造函数、两个方法以及三个属性,如代码清单3-9所示:
代码清单3-9? Car
?
package com.baobaotao.reflect;public class Car {private String brand;private String color;private int maxSpeed; //①默认构造函数public Car(){} //②带参构造函数public Car(String brand,String color,int maxSpeed){ this.brand = brand;this.color = color;this.maxSpeed = maxSpeed;} //③未带参的方法public void introduce() { System.out.println("brand:"+brand+";color:"+color+";maxSpeed:" +maxSpeed);} //省略参数的getter/Setter方法 …}
一般情况下,我们会使用如下的代码创建Car的实例:
?
Car car = new Car();car.setBrand("红旗CA72");
或者:
?
Car car = new Car("红旗CA72","黑色");
以上两种方法都采用传统方式的直接调用目标类的方法,下面我们通过Java反射机制以一种更加通用的方式间接地操作目标类:
代码清单3-10? ReflectTest
?
package com.baobaotao. reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectTest {public static Car initByDefaultConst() throws Throwable{//①通过类装载器获取Car类对象ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz = loader.loadClass("com.baobaotao.reflect.Car"); //②获取类的默认构造器对象并通过它实例化CarConstructor cons = clazz.getDeclaredConstructor((Class[])null); Car car = (Car)cons.newInstance(); //③通过反射方法设置属性Method setBrand = clazz.getMethod("setBrand",String.class); setBrand.invoke(car,"红旗CA72");Method setColor = clazz.getMethod("setColor",String.class);setColor.invoke(car,"黑色");Method setMaxSpeed = clazz.getMethod("setMaxSpeed",int.class);setMaxSpeed.invoke(car,200);return car;}public static void main(String[] args) throws Throwable {Car car = initByDefaultConst();car.introduce();}}
运行以上程序,在控制台上将打印出以下信息:
?
public class ClassLoaderTest {public static void main(String[] args) {ClassLoader loader = Thread.currentThread().getContextClassLoader();System.out.println("current loader:"+loader);System.out.println("parent loader:"+loader.getParent());System.out.println("grandparent loader:"+loader.getParent(). getParent());}}
运行以上代码,在控制台上将打出以下信息:
每一个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解以及基本Java类型(如int、double等),甚至void都拥有对应的Class对象。Class没有public的构造方法。Class对象是在装载类时由JVM通过调用类装载器中的defineClass()方法自动构造的。
Java反射机制 Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类:?? Constructor:类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。在JDK5.0中,还可以通过getConstructor(Class... parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。在JDK5.0中该方法演化为更为灵活的形式:newInstance (Object... initargs)。 ?? Method:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。在JDK5.0中可以通过getDeclaredMethod(String name, Class... parameterTypes)获取特定签名的方法,name为方法名;Class...为方法入参类型列表。Method最主要的方法是invoke(Object obj, Object[] args),obj表示操作的目标对象;args为方法入参,代码清单3 10③处演示了这个反射类的使用方法。在JDK 5.0中,该方法的形式调整为invoke(Object obj, Object... args)。此外,Method还有很多用于获取类方法更多信息的方法: ????? 1)Class getReturnType():获取方法的返回值类型;
????? 2)Class[] getParameterTypes():获取方法的入参类型数组;
????? 3)Class[] getExceptionTypes():获取方法的异常类型数组;
????? 4)Annotation[][] getParameterAnnotations():获取方法的注解信息,JDK 5.0中的新方法;?? Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredField(String name)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标对象的成员变量设置值。如果成员变量为基础类型,用户可以使用Field类中提供的带类型名的值设置方法,如setBoolean(Object obj, boolean value)、setInt(Object obj, int value)等。
此外,Java还为包提供了Package反射类,在JDK 5.0中还为注解提供了AnnotatedElement反射类。总之,Java的反射体系保证了可以通过程序化的方式访问目标类中所有的元素,对于private或protected的成员变量和方法,只要JVM的安全机制允许,也可以通过反射进行调用,请看下面的例子:
代码清单3-12? PrivateCarReflect
?
package com.baobaotao.reflect;public class PrivateCar { //①private成员变量:使用传统的类实例调用方式,只能在本类中访问 private String color; //②protected方法:使用传统的类实例调用方式,只能在子类和本包中访问 protected void drive(){ System.out.println("drive private car! the color is:"+color); }}
color变量和drive()方法都是私有的,通过类实例变量无法在外部访问私有变量、调用私有方法的,但通过反射机制却可以绕过这个限制:
代码清单3-13? PrivateCarReflect
?
…public class PrivateCarReflect { public static void main(String[] args) throws Throwable{ ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz = loader.loadClass("com.baobaotao.reflect.PrivateCar"); PrivateCar pcar = (PrivateCar)clazz.newInstance(); Field colorFld = clazz.getDeclaredField("color"); //①取消Java语言访问检查以访问private变量 colorFld.setAccessible(true); colorFld.set(pcar,"红色"); Method driveMtd = clazz.getDeclaredMethod("drive",(Class[])null); //Method driveMtd = clazz.getDeclaredMethod("drive"); JDK5.0下使用 //②取消Java语言访问检查以访问protected方法 driveMtd.setAccessible(true); driveMtd.invoke(pcar,(Object[])null); }}
运行该类,打印出以下信息:
引用drive private car! the color is:红色
在访问private、protected成员变量和方法时必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException。如果JVM的安全管理器设置了相应的安全机制,调用该方法将抛出SecurityException。
这些文章摘自于我的《Spring 3.x企业应用开发实战》,我将通过连载的方式,陆续在此发出。欢迎大家讨论。