欢迎来到王晨的博客

脚踏实地,不断坚持,没有终点,永远在路上!!!
扩大
缩小

反射与内省

一、反射 (Reflect)

在框架开发中,都是基于配置文件开发的,在配置文件中配置了类,可以通过读取配置文件中的类名,然后通过反射得到类中的所有内容,或是让类中的某个方法来执行。

也就是说,反射是在运行时获取一个类的所有信息,可以获取到 .class 的任何定义的信息(包括成员 变量,成员方法,构造器等)可以操纵类的字段、方法、构造器等部分。

 

1.1、反射的原理

我们将通过下图对反射的原理进行说明:

9171c6664cd5b9b41fe2ba5763ddcc69.png

  • 得到Class类

    1. 把 java 文件保存到本地硬盘,得到 .java。
    2. 编译 java 文件,得到 .class 文件。
    3. JVM 把 .class 文件加载到内存中,class 文件在内存中使用 Class 类表示。
  • 通过 class 文件得到 Class 类,可以通过以下 3 种方式获得 Class 类

    1. 通过成员变量获得: 类名 .class
    2. 通过具体对象获得: 对象 .getClass()
    3. 通过 Class 的静态方法获取: Class.forName("classFilePath")
  • 通过 Class 类获取 class 文件中的内容,包括:成员变量,构造方法,普通方法,它们都可以用相应的类表示:

    1. 成员方法:Field
    2. 构造方法:Constructor
    3. 普通方法:Method

 

1.2、使用反射操作属性

 

 1 public void test3() {
 2     try {
 3         Class c2 = Class.forName("cn.itcast.test09.Person");  // 得到Class类
 4         Person p11 = (Person) c2.newInstance();  // 得到Person类的对象,返回
 5         Field[] fields = c2.getDeclaredFields();  // 得到所有的属性,返回一个Field数组
 6         Field f1 = c2.getDeclaredField("name");  // 得到属性,参数是属性的名称
 7 
 8         // 如果操作的是私有的属性,不让操作,可以通过setAccessible(true)操作私有属性
 9         f1.setAccessible(true);
10         f1.set(p11, "wangwu"); // 设置name值,相当于p.name = "wangwu";
11         System.out.println(f1.get(p11)); // 相当于 p.name
12     }catch(Exception e) {
13         e.printStackTrace();
14     }
15 }

 

1.3、用反射操作构造函数

    • 无参数构造方法
      通过 Class 对象的 newInstance() 方法创建。
1 public void test1() throws Exception {
2     Class c3 = Class.forName("cn.itcast.test09.Person");
3     // 无参数的构造方法就是直接使用newInstance()方法
4     Person p = (Person) c3.newInstance();
5     p.setName("zhangsan");
6     System.out.println(p.getName());
7 }

 

  • 有参数构造方法
    不能再通过 Class 对象的 newInstance() 方法创建了,要先得到要调用的构造函数的 Consturctor 对象,然后通过 Constructor 对象的 newInstance() 方法创建。
 1 public void test2() throws Exception {
 2     Class c1 = Class.forName("cn.itcast.test09.Person");
 3 
 4     // 获取所有的构造方法
 5     Constructor[] css = c1.getConstructors();
 6     // 获取特定的构造方法:传递是有参数的构造方法里面参数类型,类型使用class的形式传递
 7     Constructor cs = c1.getConstructor(String.class, String.class);
 8 
 9     // 通过有参数的构造方法创建Person实例,而不是通过Class的对象
10     Person p1 = (Person) cs.newInstance("lisi","100");
11     System.out.println(p1.getId()+" "+p1.getName());
12 }

 

1.4、使用反射操作方法

  • 实例方法

 

 1 public void test4() throws Exception {
 2     Class c4 = Class.forName("cn.itcast.test09.Person");
 3     Person p4 = (Person) c4.newInstance();
 4 
 5     // 得到所有的普通方法
 6     Method[] mds = c4.getDeclaredMethods();
 7     // 得到特定的普通方法,传递两个参数:第一个参数:方法名称;第二个参数:方法里面参数的类型
 8     Method m1 = c4.getDeclaredMethod("setName", String.class);
 9 
10     // 使用invoke执行方法,传递两个参数:第一个参数:person实例;第二个参数:设置的值
11     // 在这里要传入person对象的原因是:我们需要知道到底是哪一个对象的setName方法执行了
12     // 如果要操作的是私有的方法 ,需要 m1.setAccessible(true);
13     m1.invoke(p4, "niuqi");
14     System.out.println(p4.getName());
15 }

 

  • 静态方法

静态方法调用方式是 类名.方法名,不需要类的实例,
所以使用反射操作静态方式时候,也是不需要实例的,
在 invoke 方法的第一个参数传入 null 即可: m1.invoke(null, "niuqi");

二、内省 (Introspector)

内省是基于反射实现的,主要用于操作 JavaBean,相比反射使用起来要方便一些。可以获取 bean 的 getter/setter 方法,也就是说,只要 JavaBean 有 getXxx() 方法,不管这个 Bean 有没有 Xxx 属性,使用内省我们都认为它有。

为了更好的理解,我们先来介绍一下 JavaBean 的规范。

2.1、JavaBean 的规范

  • 必须要有一个默认构造器。
  • 提供 get/set 方法,如果只有 get 方法,那么这个属性是只读属性。
  • 属性:有 get/set 方法的成员,还可以没有成员,只有 get/set 方法。属性名称由 get/set 方法来决定,而不是成员名称。
  • 方法名称满足一定的规范,它就是属性!boolean 类型的属性,它的读方法可以是 is 开头,也可以是 get 开头。

2.2、Introspector 操作 JavaBean

操作示例:

1 BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
2 PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();

 

每个 PropertyDescriptor 对象对应一个 JavaBean 属性:

  • String getName():获取 JavaBean 属性名称;
  • Method getReadMethod():获取属性的读方法;
  • Method getWriteMethod():获取属性的写方法。

然后再调用invoke(params...)就可以操作 JavaBean 了。

2.3、使用 BeanUtils

我们并不需要真的自己通过 Introspector 来获取 JavaBean 的实例,
我们可以通过现成的工具:
BeanUtils 来操作 JavaBean。
想要使用 BeanUtils,
我们需要先导入 commons-beanutils.jar 包,
然后,我们便可以通过 BeanUtils 来操纵 JavaBean 了。

2.3.1、设置 JavaBean 属性:BeanUtils.setProperty(bean, propertyName, propertyValue)

 

1 User user = new User();
2 BeanUtils.setProperty(user, "username", "admin");
3 BeanUtils.setProperty(user, "password", "admin123");

 

2.3.2、获取 JavaBean 属性:BeanUtils.getProperty(bean, propertyName)

1 User user = new User("admin", "admin123");
2 String username = BeanUtils.getProperty(user, "username");
3 String password = BeanUtils.getProperty(user, "password");

 

2.3.3、封装 Map 数据到 JavaBean 对象中:BeanUtils.populate(bean, map)

1 Map<String, String> map = new HashMap<String,String>();
2 map.put("username", "admin");
3 map.put("password", "admin123");
4 User user = new User();
5 BeanUtils.populate(user, map);

 

三、反射和内省的区别

反射就像给类照镜子,这个的所有信息会毫无保留的反射到镜子中,将这个类的所有信息照出来,能照出来就是有,照不出来就是没有,得到的东西都是客观真实存在的。

而内省的目的是找出 bean 的 getter 和 setter 以便操作这个 bean,
所以只要看到有 getter 或者 setter 就认为这个类有那么一个字段,
比如看到 getName() 内省就会认为这个类中有 name 字段,
但事实上并不一定会有 name。

 


来源:https://github.com/TangBean/OnlineExecutor

 

posted on 2019-01-05 10:28  王、晨  阅读(381)  评论(0编辑  收藏  举报

导航