原文来源:http://blog.csdn.net/zhang6622056/article/details/7659489
- 知道Spring在xml文件里面配置bean的方式,但是它是如何将对象赋值过去的呢?就是通过xml解析+Java反射。Xml解析可用jdom或者dom4j。网络上一找一大堆。下面我们就来说说Java的反射和内省:
- 反射:Java Reflection
- Java反射机制具有的功能:
- 1、 在运行时判断任意一个对象所属的类
- 2、 在运行时构造任意一个类的对象
- 3、 在运行时判断任意一个类具有的成员标量和方法
- 4、 在运行时调用任意一个对象的方法
- 生成动态代理
- 通俗的说:
- 反射就是让你可以通过名称来得到对象 ( 类,属性,方法 ) 的技术。
- 例如我们可以通过类名来生成一个类的实例;
- 知道了方法名,就可以调用这个方法;
- 知道了属性名就可以访问这个属性的值。
- 简单的反射案例:Class对象可以做很多操作。
- Class stu = Class.forName("cn.zhang.model.Student");
- Constructor[] stus = stu.getDeclaredConstructors();
- for(int i=0;i<stus.length;i++){
- System.out.println(stus[i].toString());
- }
- //得到实例
- Student s = (Student) stu.newInstance();
- 1..加载类有三种方法
- 加载类的3种方法
- 第一种:
- Class cls1 = Class.forName("cn.csdn.reflect.Student");
- 第二种:
- Student stu = new Student();
- Class cls2 = stu.getClass();
- 第三种:
- Class cls3 = Student.class;
- 2. 要解析下面这个类的构造方法
- public class Student {
- private String name;
- private String sex;
- private int age;
- //无参构造
- public Student(){
- }
- //带两个参数的构造
- public Student(String name,int age){
- this.name=name;
- this.age=age;
- }
- //带一个数组参数的构造
- public Student(String strs[]){
- System.out.println(strs.length);
- }
- //带一个集合类型参数的构造
- private Student(List list){
- System.out.println(list.size());
- }
- //一个普通的方法
- public void study(){
- System.out.println("good good study day day up");
- }
- public String getName(){
- return name;
- }
- }
- 通过 Constructor csr[] = cls.getConstructors();我们可以得到Student 类中所有的构造方法的参数型
- 然后我们遍历这个方法输出构造器参数的类型名称
- for(Constructor c:csr){
- //for循环新特性参数解析:(集合里面存放的个体类型 集合的单个对象(自己起名):被遍历的对象集合)
- //打印出构造器参数的类型及构造器名称
- System.out.println(c.toGenericString());
- }
- 运行的结果如下:
- public cn.csdn.reflect.Student() // 无参构造
- public cn.csdn.reflect.Student(java.lang.String,int) //带有两个参数的构造
- public cn.csdn.reflect.Student(java.lang.String[]) //带有数组参数的构造
- 当我们得到所有造器参数的类型及构造器名称我们可以通过以下的方法来解析
- // 解析无参构造:public Student()
- public void test1() throws ClassNotFoundException, SecurityException,
- NoSuchMethodException, IllegalArgumentException,
- InstantiationException, IllegalAccessException,
- InvocationTargetException {
- // 1、加载类
- Class cls = Class.forName("cn.csdn.reflect.Student");
- // 2、通过无参数的构造器解析
- // getConstructor() 方法返回一个 Constructor 对象,它反映此 Class //对象所表示的类的指定公共构造方法。
- // parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声//明顺序标识构造方法的形参类型。
- Constructor constructor = cls.getConstructor(null);
- // 3、创建类的实例 , 通过newInstance() 方法可以创建一个实例 , 这就相当
- //于 Student entity = new Student();
- Student entity = (Student) constructor.newInstance(null);
- //4、调用对象的方法
- entity.study();
- }
- //解析带有两个参数的构造:public Student(String name,int age);
- public void test2()throws Exception{
- //1、加载类
- Class cls = Class.forName("cn.csdn.reflect.Student");
- //2、通过带有参数的构造器解析
- Constructor constructor = cls.getConstructor(String.class,int.class);
- //3、创建类实例
- Student entity = (Student)constructor.newInstance("redarmy",90);
- //4、调用方法
- entity.study();
- System.out.println(entity.getName());
- }
- //解析数组参数的构造正确的写法:public Student(String strs[])
- public void test4()throws Exception{
- //1、加载类
- Class cls = Class.forName("cn.csdn.reflect.Student");
- //2、根据构造器参数类型获取相应的构造器对象
- Constructor csr = cls.getConstructor(String[].class);
- String str[]={"111","123"};
- //3、创建实体对象
- Student entity = (Student)csr.newInstance((Object)str);
- //4、调用方法
- entity.study();
- }
- //解析数组参数的构造错误的写法:public Student(String strs[])
- public void test4()throws Exception{
- //1、加载类
- Class cls = Class.forName("cn.csdn.reflect.Student");
- //2、根据构造器参数类型获取相应的构造器对象
- Constructor csr = cls.getConstructor(String[].class);
- String str[]={"111","123"};
- //3、创建实体对象
- Student entity = (Student)csr.newInstance(str);
- //4、调用方法
- entity.study();
- }
- 注意:
- 从上面两个程序我们可以发现前的的程序把这个数组做了一 Object 的造型,
- 这是因为按1.5的语法,整个数组是一个参数,而按1.4的语法,数组中的每个
- 元素是一个参数,当把一个字符串传给它时,JDK1.5 会兼容JDK1.4 的语法,
- 即会按1.4的把数组打散成为若干个单独的参数,JAVAC 只把它当成JDK1.4来解析,不把它当过1.5来解释,因此出现参数类型不对问题当我们加上 Object 时,
- 编译器会作特殊处理,编译器不把参数当成数组看,也就不会打散了。
- 但是问题出现了,我们会发现,那个集合类的构造参数没有打印出来,也就是下面的这个构造
- //带一个集合类型参数的构造
- private Student(List list){
- System.out.println(list.size());
- }
- 这时我们发现这个构造于其它三个不同,是一个私有的构造
- public void test5()throws Exception{
- //1、加载类
- Class cls = Class.forName("cn.csdn.reflect.Student");
- //2、根据构造器参数类型获取相应的构造器对象
- // getDeclaredConstructor() 这个方法是在private 中 使用的 请不要//和public 中//的 getConstructor() 混淆
- Constructor csr = cls.getDeclaredConstructor(List.class);
- // setAccessible(true) 这个方法是暴力解析 因为他是private 声明的构造方法
- csr.setAccessible(true);//暴力
- //3、创建实体对象
- Student entity = (Student)csr.newInstance(new ArrayList());
- //4、调用方法
- entity.study();
- }
- 3解析里的方法
- 我们开始解析下面这个类里的方法
- public class Student {
- //无参studay()方法
- public void studay(){
- System.out.println("学习中");
- }
- //有两个参数的getSum(int a ,int b)方法
- public int getSum(int a ,int b) {
- int sum = a + b;
- return sum;
- }
- //静态的主函数
- public static void main(String[] args) {
- System.out.println("aaaa");
- }
- }
- 我们可以通过getMethods()方法反回一个一个Method[] 类型数组,然后遍历输出
- @Test
- public void Test() throws Exception {
- Class cls = Class.forName("cn.csdn.reflect.Student");
- //用这个方法时操作类必有一个 无参构造
- Student stu = (Student) cls.newInstance();
- Method[] ms = cls.getMethods();
- for(Method m : ms) {
- System.out.println(m.toGenericString());
- }
- }
- 解析: studay() 这个方法
- @Test
- public void Test1() throws Exception{
- Class cls = Class.forName("cn.csdn.reflect.Student");
- Student stu = (Student) cls.newInstance();
- Method m = cls.getMethod("studay", null);
- m.invoke(stu, null);
- }
- 解析:getSum(int a ,int b)方法
- @Test
- public void Test2() throws Exception{
- Class cls = Class.forName("cn.csdn.reflect.Student");//加载类
- Student stu = (Student) cls.newInstance();// 创建类实例
- Method m = cls.getMethod("getSum", int.class,int.class);//2解析方法
- int sum = (Integer) m.invoke(stu, 10,10);//执行方法
- System.out.println(sum);
- }
- 解析: 主函数
- @Test
- public void Test3() throws Exception{
- Class cls = Class.forName("cn.csdn.reflect.Student");//加载类
- Student stu = (Student) cls.newInstance();// 创建类实例
- Method m = cls.getMethod("main", String[].class);//2解析方法
- m.invoke(stu, (Object)new String[]{"a"});
- }
- //简便的方法操作方法
- @Test
- public void test1()throws Exception{
- Student st = new Student();
- //通过构造器 创建 PropertyDescriptor对象
- PropertyDescriptor pd = new PropertyDescriptor("age", Student.class);
- Method md = pd.getWriteMethod(); //写操作
- md.invoke(st, 120);
- System.out.println(st.getAge());
- md = pd.getReadMethod();
- int value = (Integer)md.invoke(st, null); //读操作
- System.out.println(value);
- }
- 请大家注意这个强制转成Object这个类型,原因和构造方法的原因一样
- 4.解析里面的字段,属性如果一个字段有get方法我们就说这个字段是这个类的属性
- public class Student {
- private String pub;//属性
- private String pvt; //属性
- private String name;// 字段
- public String getPub() {
- return pub;
- }
- public String getPvt() {
- return pvt;
- }
- }
- 得到所有的字段
- @Test // 得到所有的字段getDeclaredFields()
- public void Test5() throws Exception{
- Class cls = Class.forName("cn.csdn.reflect.Student");
- Student stu = (Student) cls.newInstance();
- Field[] fd = cls.getDeclaredFields();
- for(Field f : fd) {
- System.out.println(f.getName());
- System.out.println(f.toGenericString());
- }
- }
- 对属性赋值取值
- @Test
- public void Test6() throws Exception{
- Class cls = Class.forName("cn.csdn.reflect.Student");
- Student stu = (Student) cls.newInstance();
- Field fd = cls.getDeclaredField("pvt");
- fd.setAccessible(true);
- fd.set(stu, "aa");
- String s = (String)fd.get(stu);
- System.out.println(s);
- System.out.println(stu.getPvt());
- }
- 内省(xing)
- Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。
- 当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object
- 下面我们通过来操作这个类来解释什么是内省
- public class TestSetGet {
- private int age;
- private String name;
- private String pass;
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- /*
- * 通过Introspector类获得Bean对象的 BeanInfo, 然后通过 BeanInfo 来获取属性的描述器(
- * PropertyDescriptor ) 通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,
- * 然后通过反射机制来调用这些方法。
- */
- @Test
- public void test() throws Exception{
- //得到这个类
- Class cls = Class.forName("cn.csdn.reflect.TestSetGet");
- //通过类的newInstance来获取这个类对象
- TestSetGet tsg = (TestSetGet) cls.newInstance();
- BeanInfo bi = Introspector.getBeanInfo(TestSetGet.class);
- PropertyDescriptor[] pd = bi.getPropertyDescriptors();
- for(PropertyDescriptor p : pd) {
- System.out.println(p.getName());
- if(p.getName().equals("age")) {
- Method m = p.getWriteMethod();
- m.invoke(tsg, 12);
- System.out.println("aa=== "+p.getPropertyType());
- }
- }
- System.out.println(tsg.getAge());
- }
- //简便的方法
- @Test
- public void test1()throws Exception{
- TestSetGet st = new TestSetGet();
- //通过构造器 创建 PropertyDescriptor对象
- PropertyDescriptor pd = new PropertyDescriptor("age", TestSetGet.class);
- Method md = pd.getWriteMethod(); //写操作
- md.invoke(st, 120);
- System.out.println(st.getAge());
- md = pd.getReadMethod();
- int value = (Integer)md.invoke(st, null); //读操作
- System.out.println(value);
- }