1. 程式人生 > 實用技巧 >Spring進階——反射

Spring進階——反射

引入

動態程式語言

變數並不是在計算機記憶體中被寫入的某個值,它們只是指向記憶體的“標籤”和“名稱”,所以動態程式語言的變數沒有一個固定的型別。Python

靜態程式語言

靜態程式語言的變數有固定的型別,它們指的是記憶體中的值。Java、C、C++

介紹

反射機制

動態獲取資訊以及動態呼叫物件方法的功能。

提供的功能

  • 執行時,判斷任意一個物件所屬類
  • 執行時,構造任意一個類的物件
  • 執行時,判斷任意一個類所具有的成員方法和變數
  • 執行時,呼叫任意一個物件的方法
  • 生成動態代理

反射機制原理

RTTI 執行時型別資訊

執行期間,Java通過Class物件記錄每個物件RTTI,當編寫並編譯一個新類時,就會產生一個Class物件。

Class物件是在載入類時由JVM構造的,JVM為每個類管理一個獨一無二的Class物件。

動態的生成位元組碼(.class檔案),載入到JVM中執行。

使用的類

Class類

Class物件的由來是將class檔案讀入記憶體,併為之建立一個Class物件。 例項表示正在執行的Java應用程式中的類和介面。 獲取Class物件:

  • Object.getClass();
  • Class.getSuperClass();
  • Class.forName();
  • 對於包裝器型別:類名.TYPE屬性。

Field類

提供有關類和介面的屬性資訊,以及對其動態訪問許可權。

Constructor類

提供類的單個構造方法資訊,以及對其的訪問許可權。

Method類

提供類和介面單獨的某個方法的資訊。

功能使用

獲取物件的類

  • getClass()
    //1.1 getClass()
    //獲取物件的類
    String str = "hello world!";
    cls = str.getClass();
    System.out.println(cls.getName());
    
  • Class.forName()
    • 類載入器載入方法
    //1.2  Class.forName();
    //根據具體包名來獲取類
    //字串合法命名是類的名稱空間和類的名稱組成
    cls = Class.forName("reflectprj.Student");
    

獲取類的父類

  • Class物件且該物件有父類
    //1.3 getSuperclass();
    //獲取類的父類
    Class superCls = cls.getSuperclass();
    

獲取類的構造方法

  • 獲取全部的構造方法getDeclaredConstructors()
  • 獲取特定的構造方法getDeclaredConstructor(Class[] class)
  • 建立構造方法建立例項newInstance(引數)
    //3.1 getDeclaredConstructors();
    //獲取類的構造方法
    Constructor[] constructors;
    constructors = cls.getDeclaredConstructors();
    for(Constructor constructor:constructors) {
          System.out.println(constructor.toString());
    }
    //3.1.2 
    //獲取類特定的構造方法
    
    Class[] var = {int.class,String.class,String.class};
    Constructor constructor = cls.getDeclaredConstructor(var);
    System.out.println(constructor.toString());
    
    //4.1 newInstance();
    //通過構造方法建立例項
    Student stu = (Student)constructor.newInstance(1,"王小明","man");
    System.out.println(stu.toString());
    

獲取類的方法

  • 獲取全部的方法getDeclaredMethods()
  • 獲取特定的方法getDeclaredMethod("方法名",Class[])
  • 將物件與方法關聯,並呼叫方法。invoke(物件名,引數)
  • 如果方法為私有,則呼叫method.setAccessible(true)
    //3.2 getDeclaredMethod();
    //獲取類的方法
    Method [] ms = cls.getDeclaredMethods();
    for(Method m:ms) {
        System.out.println(m.getName());
    }
    
    //4.2 invoke();
    //呼叫類的方法
    Method m = cls.getDeclaredMethod("setSex", String.class);
    m.invoke(stu, "women");
    System.out.println(stu.toString());
    
    //5 setAccessible()
    //呼叫私有方法
    m = cls.getDeclaredMethod("print", null);
    m.setAccessible(true);
    m.invoke(stu,null);
    

獲取類的屬性

  • 獲取全部的屬性getDeclaredFields()
  • 獲取特定的屬性getDeclaredField("fieldName")
  • 將物件與屬性關聯,並設定屬性set(物件名,屬性)
  • 將物件與屬性關聯,獲取屬性get(物件名)
  • 如果屬性為私有,則呼叫method.setAccessible(true)
    //3.3 getDeclaredField()
    //獲取類的屬性
    Field fs[] = cls.getDeclaredFields();
    for(Field f:fs) {
          System.out.println(f.getName());
    }
    
    //4.3 set();
    //設定私有類屬性
    Field f = cls.getDeclaredField("sex");
    f.setAccessible(true);
    f.set(stu,"men");
    System.out.println(f.get(stu).toString());
    

反射的應用

操作資料庫

動態建立SQL語句。

public void save(Object obj) {
      String sql = "insert into ";
      try {
            Class cls = obj.getClass();
            String clsName = cls.getSimpleName();
            sql+=clsName;
            sql+="(";
            Field fs[] = cls.getDeclaredFields();
            for(Field f:fs) {
                  sql+=f.getName()+",";
            }
            sql = sql.substring(0, sql.length()-1);
            sql+=") values(";
            Field fs1[] = cls.getDeclaredFields();
            for(Field f:fs1) {
                  f.setAccessible(true);
                  sql+=f.get(obj)+",";
            }
            sql = sql.substring(0, sql.length()-1);
            sql+=")";
      }catch(Exception e) {
            e.printStackTrace();
      }
      System.out.println(sql);
}

解析XML

解析XML動態生成物件。(Spring框架)

動態代理

動態代理

框架使用

  • Spring框架
  • Hibernate框架
  • Struts框架

缺點

反射對於效能有影響,反射基本上是一種解釋操作。 可以告訴JVM希望做什麼,讓其滿足要求。 這類操作慢於直接執行相同的操作。