java反射的使用以及在Mybatis源码中的应用


前言

java反射的概念以及简单使用已经在java反射机制简单应用中提到,今天这篇文章来进一步介绍java反射及其java反射在一些框架中的应用


一、反射的使用

java给我们提供了非常完整的API来对类的结构进行操作

 /*反射使用*/
       /* 1、获得类对象aClass,aClass中有类的定义的结构,可以访问和操作类的方法和属性
       * (1)类的属性:类对象可以提供对类的静态属性的访问。通过类对象,可以获取和修改类的静态字段值。
       * getDeclaredField():得到当前该类的属性,不能直接用getField,因为会得到该类和其父类(Object)的所有属性
         (2)类的方法:类对象可以提供对类的静态方法的访问。可以使用类对象调用类的静态方法,无需实例化类的对象。
       * getDeclaredMethod(String name, Class<?>... parameterTypes):得到当前该类的方法,同样的,也不能直接用getMethod
         (3)类的构造函数:类对象可以提供对类的构造函数的访问。可以使用类对象创建类的对象,实例化类。
       * getDeclaredConstructors():得到该类的所有构造方法
       * getConstructors:得到该类公共的构造方法
       * getDeclaredConstructor(Class<?>... parameterTypes):得到该类特定的构造方法,通过参数列表来确定
         (4)类的注解:类对象可以返回类的注解信息,包括在类级别上声明的注解。
       * getAnnotations():得到所有该类的注解,一个注解数组
       * getAnnotation(Class<T> annotationClass):得到特定的注解
         (5)类的接口信息:类对象可以返回实现的接口信息,包括继承的接口和实现的接口。
       * getInterfaces():得到该类的实现的所有接口,一个接口数组
       * 如果需要得到特定的接口信息,则需要遍历接口数组,加以判断:
       *  for (Type anInterface : interfaces) {
            // 判断是否为特定接口
            if (anInterface == MyInterface1.class) {
             ......
              }
         }
       * getInterfaces():得到接口的父接口信息
         (6)类的父类信息:类对象可以返回直接父类的相关信息,包括父类的名称和类型。
       * getSuperclass()
         (7)类的修饰符和访问级别:类对象可以提供对类的修饰符(如public、private、abstract等)和访问级别(如public、protected、private等)的访问。
       * getModifiers():得到类的访问修饰符
       *    // 判断修饰符
        if (Modifier.isPublic(modifiers)) {
            System.out.println("Modifier: public");
        } else if (Modifier.isAbstract(modifiers)) {
            System.out.println("Modifier: abstract");
        } else if (Modifier.isFinal(modifiers)) {
            System.out.println("Modifier: final");
        } else {
            System.out.println("Modifier: unknown");
        }
       * getPackage():得到类的访问级别(其实就是得到类的包名)
         * */
===============================================================================================
/*
*我们先定义一个普通的Person类
*/
package com.darkForest.test.reflex;

public class Person {
    private int id=1;
    private String name;
    private String password;

    public Person() {
    }

    public Person(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public void show(String name,String password){
        System.out.println(this.name+password);
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
===============================================================================================
/*
*接下来得到Person的类对象
*/
  Class<?> aClass = Class.forName("com.darkForest.test.reflex.Person");
//对Person类的构造方法的操作,可对Person进行实例化
 Person person = (Person) aClass.newInstance();//无参构造方法的方式
        Constructor<?> constructor=aClass.getConstructor(String.class,String.class);//含参构造方法的方式
        Person object = (Person) constructor.newInstance("zhaa", "12345");
//对Person类的属性的操作
//获得属性对象
        Field name = aClass.getDeclaredField("name");
       /*对私有属性赋值时,需要设置权限,不然对属性的操作会报
       * class com.darkForest.test
       * .reflex.Test cannot access a member
       * of class com.darkForest.test.reflex.Person with modifiers "private"
       * 这个错误*/
        name.setAccessible(true);
        //给属性赋值
        name.set(person,"fff");
//对Person类的方法的操作
//获取方法对象
        Method setName = aClass.getDeclaredMethod("setName", String.class);
        //invoke:调用person对象指定的方法
        setName.invoke(person,"hhh");

二、手写Mybatis源码时对Java反射的应用

在手写mybatis源码时,处理从数据库中查询到的结果集变成java的数据类型(实体类对象、map、list(Object)、list<Map<String,Object>>)等的时候就需要用到反射来对类的方法进行获取和操作,我这里采用的是全参构造方法的方式来对实体类进行创建并赋值,大家可以看下面的代码:

public <T>T getEntity(Class clazz) throws InstantiationException, IllegalAccessException, SQLException, NoSuchMethodException, InvocationTargetException {
        List<Object> list=null;
        while (resultSet.next()) {
            //得到所有属性
            Field[] fields=clazz.getDeclaredFields();
            //数组来保存参数类型
            Class<?> [] parameterTypes=new Class[fields.length];
            //遍历所有属性,得到属性数据类型
            for (int i = 0; i < fields.length; i++) {
                parameterTypes[i]=fields[i].getType();
            }
           //使用含参构造函数实例化对象
            Constructor<?> constructor=clazz.getConstructor(parameterTypes);
            //获取了结果集的元数据信息。结果集中列的信息,比如列的名称、数据类型、列的数量  getMetaData:得到(元数据对象)
            ResultSetMetaData md = resultSet.getMetaData();
            //得到结果集数据记录(列)数
            int columnCount = md.getColumnCount();
            //数组,用来暂时存储每一条记录的值
            Object[] values=new Object[columnCount];
            for (int i = 1; i <=columnCount; i++) {
                //得到当前(i)对应的一条记录对象
                Object val=resultSet.getObject(i);
                //获取与查询结果集中指定列(对应的i)关联的目录名称
                String catalogName = md.getCatalogName(i);
                Object value=resultSet.getObject(i);
                values[i-1]=value;
            }
            //采用全参的方式实例化,给实体类中每个属性都赋值
            Object object = constructor.newInstance(values);
            //把实例化的对象存进list集合里
            list.add(object);
        }
        return (T)list;
    }

采用这种方式会有一个弊端,就是必需查询数据库表中的所有列的数据,如果只查询其中的一些列的数据,那么实体类中没有与之对应的属性就会为空,而且是采用构造函数的方式,参数的顺序没有做处理,如果直接把参数数组放进去,就会有可能导致参数数组类型顺序、长度和构造函数参数数组类型顺序、长度不一致的情况,比如把结果集中的name的值赋值给实体类中的password等等。

更好的解决方法是在遍历结果集的所有列时,获取当前列的名称和类型,再找到实体类中与之相对应的方法,把数据依次存进实体类中,如果对这种方式有兴趣,可以看一下我的另一篇博文手写Mybatis源码01

总结

java的反射是一种很强大的机制,是多数框架的基础,对于学习和使用甚至开发框架是必不可少的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值