1. 程式人生 > 其它 >mybaits原始碼分析--反射模組(二)

mybaits原始碼分析--反射模組(二)

1.反射模組

MyBatis在進行引數處理、結果集對映等操作時會使用到大量的反射操作,Java中的反射功能雖然強大,但是程式碼編寫起來比較複雜且容易出錯,為了簡化反射操作的相關程式碼,MyBatis提供了專門的反射模組,該模組位於org.apache.ibatis.reflection包下,它對常見的反射操作做了進一步的封裝,提供了更加簡潔方便的反射API。

1.1 Reflector

Reflector是反射模組的基礎,每個Reflector物件都對應一個類,在Reflector中快取了反射需要使用的類的元資訊

1.1.1 屬性

首先看下Reflector類中提供的相關屬性的含義

  //
對應的Class 型別 1 private final Class<?> type; // 可讀屬性的名稱集合 可讀屬性就是存在 getter方法的屬性,初始值為null private final String[] readablePropertyNames; // 可寫屬性的名稱集合 可寫屬性就是存在 setter方法的屬性,初始值為null private final String[] writablePropertyNames; // 記錄了屬性相應的setter方法,key是屬性名稱,value是Invoker方法 // 他是對setter方法對應Method物件的封裝
private final Map<String, Invoker> setMethods = new HashMap<>(); // 屬性相應的getter方法 private final Map<String, Invoker> getMethods = new HashMap<>(); // 記錄了相應setter方法的引數型別,key是屬性名稱 value是setter方法的引數型別 private final Map<String, Class<?>> setTypes = new HashMap<>();
// 和上面的對應 private final Map<String, Class<?>> getTypes = new HashMap<>(); // 記錄了預設的構造方法 private Constructor<?> defaultConstructor; // 記錄了所有屬性名稱的集合 private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

1.1.2 構造方法

在Reflector的構造器中會完成相關的屬性的初始化操作

  // 解析指定的Class型別 並填充上述的集合資訊
  public Reflector(Class<?> clazz) {
    type = clazz; // 初始化 type欄位
    addDefaultConstructor(clazz);// 設定預設的構造方法
    addGetMethods(clazz);// 獲取getter方法
    addSetMethods(clazz); // 獲取setter方法
    addFields(clazz); // 處理沒有getter/setter方法的欄位
    // 初始化 可讀屬性名稱集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    // 初始化 可寫屬性名稱集合
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    // caseInsensitivePropertyMap記錄了所有的可讀和可寫屬性的名稱 也就是記錄了所有的屬性名稱
    for (String propName : readablePropertyNames) {
      // 屬性名稱轉大寫
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      // 屬性名稱轉大寫
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

反射我們也可以在專案中我們直接拿來使用,定義一個普通的Bean物件。

1.1.3 公共的API方法

然後看看Reflector中提供的公共的API方法

方法名稱 作用
getType 獲取Reflector表示的Class
getDefaultConstructor 獲取預設的構造器
hasDefaultConstructor 判斷是否有預設的構造器
getSetInvoker 根據屬性名稱獲取對應的Invoker 物件
getGetInvoker 根據屬性名稱獲取對應的Invoker物件
getSetterType

獲取屬性對應的型別 比如:
String name; // getSetterType("name") --> java.lang.String

getGetterType 與上面是對應的
getGetablePropertyNames 獲取所有的可讀屬性名稱的集合
getSetablePropertyNames 獲取所有的可寫屬性名稱的集合
hasSetter 判斷是否具有某個可寫的屬性
hasGetter 判斷是否具有某個可讀的屬性
findPropertyName 根據名稱查詢屬性

瞭解了Reflector物件的基本資訊後接下需要知道的就是如何來獲取Reflector物件,在MyBatis中給我們提供了一個ReflectorFactory工廠物件。所以先來簡單瞭解下ReflectorFactory物件,當然也可以直接new 出來,像上面的案例一樣

1.2 ReflectorFactory

ReflectorFactory介面主要實現了對Reflector物件的建立和快取。

1.2.1 ReflectorFactory介面的定義

介面的定義如下

public interface ReflectorFactory {
  // 檢測該ReflectorFactory是否快取了Reflector物件
  boolean isClassCacheEnabled();
  // 設定是否快取Reflector物件
  void setClassCacheEnabled(boolean classCacheEnabled);
  // 建立指定了Class的Reflector物件
  Reflector findForClass(Class<?> type);
}

然後看看它的具體實現

DefaultReflectorFactory中的實現,程式碼比較簡單

public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  // 實現對 Reflector 物件的快取
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {// 開啟快取
      // synchronized (type) removed see issue #461
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      // 沒有開啟快取就直接建立
      return new Reflector(type);
    }
  }

}

1.2.3 使用演示

public class User implements Serializable {
    private Integer id;

    private String userName;

    private String realName;

    private String password;

    private Integer age;

    private Integer dId;

    private Dept dept;

    public Integer getId() {
        return 2;
    }

    public void setId(Integer id) {
        System.out.println(id);
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getdId() {
        return dId;
    }

    public void setdId(Integer dId) {
        this.dId = dId;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}
    /**
     * ReflectorFactory功能演示
     * @throws Exception
     */
    @Test
    public void test01() throws Exception{
        ReflectorFactory factory=new DefaultReflectorFactory();
        Reflector reflector=factory.findForClass(User.class);
        System.out.println("可讀"+ Arrays.toString(reflector.getGetablePropertyNames()));
        System.out.println("可寫"+ Arrays.toString(reflector.getSetablePropertyNames()));
        System.out.println("是否有預設構造器"+reflector.hasDefaultConstructor());
        System.out.println("Reflector對應的class"+reflector.getType());
    }

1.3 Invoker

針對於Class中Field和Method的呼叫,在MyBatis中封裝了Invoker物件來統一處理(有使用到介面卡模式)

1.3.1 介面說明

public interface Invoker {
  // 執行Field或者Method
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  // 返回屬性相應的型別
  Class<?> getType();
}

這個介面對應有三個實現

1.3.2 效果演示

public class User {

    public Integer getId() {
        System.out.println("讀取id");
        return 6;
    }
    public void setId(Integer id) {
        System.out.println("寫入id:"+id);
    }
    public String getUserName() {
        return "張三";
    }
}
    @Test
public void test02() throws Exception{
ReflectorFactory factory = new DefaultReflectorFactory();
Reflector reflector = factory.findForClass(User.class);
//獲取構造器 生成對應物件
Object o = reflector.getDefaultConstructor().newInstance();
org.apache.ibatis.reflection.invoker.Invoker invoker1=reflector.getSetInvoker("id");
invoker1.invoke ( o ,new Object[] { 999 });

/* 讀取 */
org.apache.ibatis.reflection.invoker.Invoker invoker2= reflector.getGetInvoker("id");
invoker2.invoke( o,null);
}
}

1.4 MetaClass

在Reflector中可以針對普通的屬性操作,但是如果出現了比較複雜的屬性,比如 private Person person; 這種,我們要查詢的表示式 person.userName.針對這種表示式的處理,這時就可以通過MetaClass來處理了。我們來看看主要的屬性和構造方法

/**
 * 通過 Reflector 和 ReflectorFactory 的組合使用 實現對複雜的屬性表示式的解析
 * @author Clinton Begin
 */
public class MetaClass {
  // 快取 Reflector
  private final ReflectorFactory reflectorFactory;
  // 建立 MetaClass時 會指定一個Class reflector會記錄該類的相關資訊
  private final Reflector reflector;

  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }

  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }

  public MetaClass metaClassForProperty(String name) {
    Class<?> propType = reflector.getGetterType(name);
    return MetaClass.forClass(propType, reflectorFactory);
  }

  public String findProperty(String name) {
    StringBuilder prop = buildProperty(name, new StringBuilder());
    return prop.length() > 0 ? prop.toString() : null;
  }

  public String findProperty(String name, boolean useCamelCaseMapping) {
    if (useCamelCaseMapping) {
      name = name.replace("_", "");
    }
    return findProperty(name);
  }

  public String[] getGetterNames() {
    return reflector.getGetablePropertyNames();
  }

  public String[] getSetterNames() {
    return reflector.getSetablePropertyNames();
  }

  public Class<?> getSetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaClass metaProp = metaClassForProperty(prop.getName());
      return metaProp.getSetterType(prop.getChildren());
    } else {
      return reflector.getSetterType(prop.getName());
    }
  }

  public Class<?> getGetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaClass metaProp = metaClassForProperty(prop);
      return metaProp.getGetterType(prop.getChildren());
    }
    // issue #506. Resolve the type inside a Collection Object
    return getGetterType(prop);
  }

  private MetaClass metaClassForProperty(PropertyTokenizer prop) {
    Class<?> propType = getGetterType(prop);
    return MetaClass.forClass(propType, reflectorFactory);
  }

  private Class<?> getGetterType(PropertyTokenizer prop) {
    Class<?> type = reflector.getGetterType(prop.getName());
    if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
      Type returnType = getGenericGetterType(prop.getName());
      if (returnType instanceof ParameterizedType) {
        Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
        if (actualTypeArguments != null && actualTypeArguments.length == 1) {
          returnType = actualTypeArguments[0];
          if (returnType instanceof Class) {
            type = (Class<?>) returnType;
          } else if (returnType instanceof ParameterizedType) {
            type = (Class<?>) ((ParameterizedType) returnType).getRawType();
          }
        }
      }
    }
    return type;
  }

  private Type getGenericGetterType(String propertyName) {
    try {
      Invoker invoker = reflector.getGetInvoker(propertyName);
      if (invoker instanceof MethodInvoker) {
        Field _method = MethodInvoker.class.getDeclaredField("method");
        _method.setAccessible(true);
        Method method = (Method) _method.get(invoker);
        return TypeParameterResolver.resolveReturnType(method, reflector.getType());
      } else if (invoker instanceof GetFieldInvoker) {
        Field _field = GetFieldInvoker.class.getDeclaredField("field");
        _field.setAccessible(true);
        Field field = (Field) _field.get(invoker);
        return TypeParameterResolver.resolveFieldType(field, reflector.getType());
      }
    } catch (NoSuchFieldException | IllegalAccessException ignored) {
    }
    return null;
  }

  public boolean hasSetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      if (reflector.hasSetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop.getName());
        return metaProp.hasSetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasSetter(prop.getName());
    }
  }

  public boolean hasGetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      if (reflector.hasGetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop);
        return metaProp.hasGetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasGetter(prop.getName());
    }
  }

  public Invoker getGetInvoker(String name) {
    return reflector.getGetInvoker(name);
  }

  public Invoker getSetInvoker(String name) {
    return reflector.getSetInvoker(name);
  }

  private StringBuilder buildProperty(String name, StringBuilder builder) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        builder.append(propertyName);
        builder.append(".");
        MetaClass metaProp = metaClassForProperty(propertyName);
        metaProp.buildProperty(prop.getChildren(), builder);
      }
    } else {
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

  public boolean hasDefaultConstructor() {
    return reflector.hasDefaultConstructor();
  }

}

效果演示,準備Bean物件

public class RichType {
    private RichType richType;
    private String richField;
    private String richProperty;
    private Map richMap = new HashMap ();
    private List richList = new ArrayList () {
        {
            add("小傢伙要記得學習");
        }
    };
    public RichType getRichType() {
        return richType;
    }
    public void setRichType(RichType richType) {
        this.richType = richType;
    }
    public String getRichProperty() {
        return richProperty;
    }
    public void setRichProperty(String richProperty) {
        this.richProperty = richProperty;
    }
    public List getRichList() {
        return richList;
    }
    public void setRichList(List richList) {
        this.richList = richList;
    }
    public Map getRichMap() {
        return richMap;
    }
    public void setRichMap(Map richMap) {
        this.richMap = richMap;
    }
}
  /**
     * MetaClass功能演示
     * @throws Exception
     */
    @Test
    public void test03() throws Exception{
        ReflectorFactory factory = new DefaultReflectorFactory();
        MetaClass metaClass=MetaClass.forClass ( RichType.class,factory);
        System.out.println(metaClass.hasGetter("richField"));
        System.out.println(metaClass.hasGetter("richProperty"));
        System.out.println(metaClass.hasGetter("richList"));
        System.out.println(metaClass.hasGetter("richMap"));
        System.out.println(metaClass.hasGetter("richList[0]"));

        System.out.println(metaClass.hasGetter("richType"));
        System.out.println(metaClass.hasGetter("richType.richField"));
        System.out.println(metaClass.hasGetter("richType.richProperty"));
        System.out.println(metaClass.hasGetter("richType.richList"));
        System.out.println(metaClass.hasGetter("richType.richMap"));
        System.out.println(metaClass.hasGetter("richType.richList[0]"));
         // findProperty 只能處理 . 的表示式
        System.out.println(metaClass.findProperty("richType.richProperty"));
        System.out.println(metaClass.findProperty("richType.richProperty1"));
        System.out.println(metaClass.findProperty("richList[0]"));
        System.out.println(Arrays.toString(metaClass.getGetterNames()));
    }

1.5 MetaObject

可以通過MetaObject物件解析複雜的表示式來對提供的物件進行操作。具體的通過案例來演示會更直觀些

    @Test
    public void test04() throws Exception{
        RichType richType=new RichType ();
        MetaObject metaObject=SystemMetaObject.forObject ( richType );
        metaObject.setValue ( "richField","李四" );
        System.out.println (metaObject.getValue ( "richField" ));
    }
    @Test
    public void test05() throws Exception{
        RichType richType=new RichType ();
        MetaObject metaObject=SystemMetaObject.forObject ( richType );
        metaObject.setValue ( "richType.richField","李四1" );
        System.out.println (metaObject.getValue ( "richType.richField" ));
    }
    @Test
    public void test06() throws Exception{
        RichType richType=new RichType ();
        MetaObject metaObject=SystemMetaObject.forObject ( richType );
        metaObject.setValue ( "richMap[0]","李四2" );
        System.out.println (metaObject.getValue ( "richMap[0]" ));
    }

1.6 反射模組應用

看下在MyBatis的核心處理層中的實際應用

1.6.1 SqlSessionFactory

在建立SqlSessionFactory操作的時候會完成Configuration物件的建立,而在Configuration中預設定義的ReflectorFactory的實現就是DefaultReflectorFactory物件

然後在解析全域性配置檔案的程式碼中,給使用者提供了ReflectorFactory的擴充套件,也就是我們在全域性配置檔案中可以通過<reflectorFactory>標籤來使用我們自定義的ReflectorFactory

1.6.2 執行SQL

在Statement獲取結果集後,在做結果集對映的使用有使用到,在DefaultResultSetHandler的createResultObject方法中。

然後在DefaultResultSetHandler的getRowValue方法中在做自動對映的時候

繼續跟蹤,在createAutomaticMappings方法中

這短短的一生我們最終都會失去,不妨大膽一點,愛一個人,攀一座山,追一個夢