一次項目中的反射機制的使用

什么是Java的反射機制

JAVA反射機制是在運行狀態(tài)中, 對于任意一個類, 都能夠知道這個類的所有屬性和方法; 對于任意一個對象, 都能夠調(diào)用它的任意一個方法和屬性; 這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制.

主要作用有三:

  1. 運行時取得類的方法和字段的相關(guān)信息。

  2. 創(chuàng)建某個類的新實例(.newInstance())

  3. 取得字段引用直接獲取和設(shè)置對象字段,無論訪問修飾符是什么。

用處如下:

  1. 觀察或操作應用程序的運行時行為。
  2. 調(diào)試或測試程序,因為可以直接訪問方法、構(gòu)造函數(shù)和成員字段。
  3. 通過名字調(diào)用不知道的方法并使用該信息來創(chuàng)建對象和調(diào)用方法

反射的應用場景

我在車管項目中遇到了一個應用場景,請先看界面:

1.gif

在這個界面中,我要通過輸入關(guān)鍵字來檢索已經(jīng)通過網(wǎng)絡請求加載好的列表數(shù)據(jù)中,有哪些數(shù)據(jù)項是符合我的過濾條件的。

這個需求感覺很簡單,只要遍歷存放數(shù)據(jù)的List<T>,然后每項都對比關(guān)鍵字即可,然后再將過濾后的數(shù)據(jù)存入一個新的List<T>中,返回給RecyclerView的Adapter進行數(shù)據(jù)刷新即可。

但是在做的過程中我發(fā)現(xiàn)了兩個問題:

1.第一個問題是我們的List<T>獲取的數(shù)據(jù)由于業(yè)務的不同,其泛型也是不同的,且泛型之間沒有關(guān)聯(lián)性;
2.每個泛型都是一個Bean,我們要過濾的是Bean中的屬性,這個也是不確定的;

但是項目中大量的用到了這個功能,幾乎每個列表項都會有本地過濾的功能,如果不將功能進行封裝,每次都寫硬編碼,項目的復用性太低了,且整體項目不易維護。

一開始我想通過泛型進行處理,但是鑒于上邊兩個問題,泛型是沒辦法靈活處理的,尤其是第二個問題,泛型沒有辦法處理,于是想來想去,想到了反射機制。

閑話少說,先看代碼:

public class ListFilter<T> {

    /**
     * 過濾ArrayList中的關(guān)鍵字數(shù)據(jù)
     * @param models 網(wǎng)絡獲取到的數(shù)據(jù)列表
     * @param query 過濾關(guān)鍵字
     * @param propertyName 泛型的數(shù)據(jù)過濾項
     * @return 返回過濾后的數(shù)據(jù)
     */
    public ArrayList<T> filter(ArrayList<T> models, String query, String propertyName) {

        ArrayList<T> filteredModelList = null;
        //實例化這個類賦給o
        try {
            query = query.toLowerCase();
            filteredModelList = new ArrayList<>();
            for (T model : models) {
                final String text = getClassInfo(model,model.getClass().getName(),propertyName).toLowerCase();
                if (text.contains(query)) {
                    filteredModelList.add(model);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filteredModelList;
    }

    /**
     *  獲取類的屬性值
     * @param obj 類實例
     * @param classNameString 類名
     * @param propertyNameString 獲取的屬性名
     * @return  屬性值
     */
    private  String getClassInfo(Object obj,String classNameString,String propertyNameString) {
        String returnString="";
        try{
            Class classInfo = Class.forName(classNameString);
            if(!(classInfo.isInstance(obj))){
                L.e("傳入的java實例與配置的java對象類型不符!");
                return returnString;
            }
            Field field = classInfo.getDeclaredField(propertyNameString);
            field.setAccessible(true);
            returnString=field.get(obj).toString();
        }catch(Exception e){
            e.printStackTrace();
        }
        return returnString;
    }
}

這段代碼其實就是getClassInfo方法為核心的獲取某個類的某個字段的字段值,這里涉及幾個知識點大家可以自行百度學習一下:

1.getCanonicalName(), getName(), getSimpleName()三個方法的不同?
簡答:
1、getCanonicalName() 是獲取所傳類從java語言規(guī)范定義的格式輸出。
2、getName() 是返回實體類型名稱
3、getSimpleName() 返回從源代碼中返回實例的名稱。

2.反射中獲取類中的私有屬性該如何操作?
簡答:
field.setAccessible(true);
public void setAccessible(boolean flag) throws SecurityException
將此對象的 accessible 標志設(shè)置為指示的布爾值。值為 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。值為 false 則指示反射的對象應該實施 Java 語言訪問檢查。
實際上setAccessible是啟用和禁用訪問安全檢查的開關(guān),并不是為true就能訪問為false就不能訪問;

反射經(jīng)典應用場景

以上就是我的項目中,使用反射的一個具體的實例了,其實在Java中還有很多地方都用到了反射,這里舉兩個比較常見的例子:

  • JDBC 的數(shù)據(jù)庫的連接

在JDBC 的操作中,如果要想進行數(shù)據(jù)庫的連接,則必須按照以上的幾步完成

  1. 通過Class.forName()加載數(shù)據(jù)庫的驅(qū)動程序 (通過反射加載,前提是引入相關(guān)了Jar包)
  2. 通過 DriverManager 類進行數(shù)據(jù)庫的連接,連接的時候要輸入數(shù)據(jù)庫的連接地址、用戶名、密碼
  3. 通過Connection 接口接收連接
public class ConnectionJDBC {  
  
    /** 
     * @param args 
     */  
    //驅(qū)動程序就是之前在classpath中配置的JDBC的驅(qū)動程序的JAR 包中  
    public static final String DBDRIVER = "com.mysql.jdbc.Driver";  
    //連接地址是由各個數(shù)據(jù)庫生產(chǎn)商單獨提供的,所以需要單獨記住  
    public static final String DBURL = "jdbc:mysql://localhost:3306/test";  
    //連接數(shù)據(jù)庫的用戶名  
    public static final String DBUSER = "root";  
    //連接數(shù)據(jù)庫的密碼  
    public static final String DBPASS = "";  
      
      
    public static void main(String[] args) throws Exception {  
        Connection con = null; //表示數(shù)據(jù)庫的連接對象  
        Class.forName(DBDRIVER); //1、使用CLASS 類加載驅(qū)動程序 ,反射機制的體現(xiàn) 
        con = DriverManager.getConnection(DBURL,DBUSER,DBPASS); //2、連接數(shù)據(jù)庫  
        System.out.println(con);  
        con.close(); // 3、關(guān)閉數(shù)據(jù)庫  
    }  
  • Spring 框架的使用

在 Java的反射機制在做基礎(chǔ)框架的時候非常有用,行內(nèi)有一句這樣的老話:反射機制是Java框架的基石。一般應用層面很少用,不過這種東西,現(xiàn)在很多開源框架基本都已經(jīng)封裝好了,自己基本用不著寫。典型的除了hibernate之外,還有spring也用到很多反射機制。最經(jīng)典的就是xml的配置模式。

Spring 通過 XML 配置模式裝載 Bean 的過程:

  1. 將程序內(nèi)所有 XML 或 Properties 配置文件加載入內(nèi)存中
  2. Java類里面解析xml或properties里面的內(nèi)容,得到對應實體類的字節(jié)碼字符串以及相關(guān)的屬性信息
  3. 使用反射機制,根據(jù)這個字符串獲得某個類的Class實例
  4. 動態(tài)配置實例的屬性

Spring這樣做的好處是:

  • 不用每一次都要在代碼里面去new或者做其他的事情
  • 以后要改的話直接改配置文件,代碼維護起來就很方便了
  • 有時為了適應某些需求,Java類里面不一定能直接調(diào)用另外的方法,可以通過反射機制來實現(xiàn)

模擬 Spring 加載 XML 配置文件:

public class BeanFactory {
       private Map<String, Object> beanMap = new HashMap<String, Object>();
       /**
       * bean工廠的初始化.
       * @param xml xml配置文件
       */
       public void init(String xml) {
              try {
                     //讀取指定的配置文件
                     SAXReader reader = new SAXReader();
                     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                     //從class目錄下獲取指定的xml文件
                     InputStream ins = classLoader.getResourceAsStream(xml);
                     Document doc = reader.read(ins);
                     Element root = doc.getRootElement();  
                     Element foo;
                    
                     //遍歷bean
                     for (Iterator i = root.elementIterator("bean"); i.hasNext();) {  
                            foo = (Element) i.next();
                            //獲取bean的屬性id和class
                            Attribute id = foo.attribute("id");  
                            Attribute cls = foo.attribute("class");
                           
                            //利用Java反射機制,通過class的名稱獲取Class對象
                            Class bean = Class.forName(cls.getText());
                           
                            //獲取對應class的信息
                            java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
                            //獲取其屬性描述
                            java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
                            //設(shè)置值的方法
                            Method mSet = null;
                            //創(chuàng)建一個對象
                            Object obj = bean.newInstance();
                           
                            //遍歷該bean的property屬性
                            for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {  
                                   Element foo2 = (Element) ite.next();
                                   //獲取該property的name屬性
                                   Attribute name = foo2.attribute("name");
                                   String value = null;
                                  
                                   //獲取該property的子元素value的值
                                   for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
                                          Element node = (Element) ite1.next();
                                          value = node.getText();
                                          break;
                                   }
                                  
                                   for (int k = 0; k < pd.length; k++) {
                                          if (pd[k].getName().equalsIgnoreCase(name.getText())) {
                                                 mSet = pd[k].getWriteMethod();
                                                 //利用Java的反射極致調(diào)用對象的某個set方法,并將值設(shè)置進去
                                                 mSet.invoke(obj, value);
                                          }
                                   }
                            }
                           
                            //將對象放入beanMap中,其中key為id值,value為對象
                            beanMap.put(id.getText(), obj);
                     }
              } catch (Exception e) {
                     System.out.println(e.toString());
              }
       }
      
       //other codes
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • 虛偽和謊言閃現(xiàn) 即便換了一副谷漏子 臭是會堆積的 花兒的容顏 芬芳蓋不住腐朽的元 果核爛掉 蛆蟲在爬 捧著蜂蜜 暗...
    不悲傷a不仰望閱讀 1,001評論 18 15
  • 姓名:羅江漢 公司:東莞市旭瑞光電科技有限公司 組別:第230期努力二組 【日精進打卡第 16天】 【知~學習】 ...
    羅江漢閱讀 160評論 0 0
  • 理財不僅僅是單純的賺錢,其實是對生活的有效管理。 學習中學到的觀點、思維是可以用到生活的方方面面的,從而做事更加的...
    hello貢閱讀 309評論 0 1

友情鏈接更多精彩內(nèi)容