Reflection:Java反射機制的應用場景

近期在維護公司項目的時候遇到一個問題,由於實體類中的 set 方法涉及到了業務邏輯,所以在給對象賦值的過程當中不可以使用 set 方法,爲了實現功能,因此採用了反射的機制給對象屬性賦值,藉此機會也瞭解了反射的一些具體用法和使用場景,分如下兩點對反射進行分析:java

  • 反射的優點和劣勢
  • 反射的應用場景

反射的優點和劣勢

  我的理解,反射機制實際上就是上帝模式,若是說方法的調用是 Java 正確的打開方式,那反射機制就是上帝偷偷開的後門,只要存在對應的class,一切都可以被調用。node

  那上帝爲何要打開這個後門呢?這涉及到了靜態和動態的概念mysql

  • 靜態編譯:在編譯時肯定類型,綁定對象
  • 動態編譯:運行時肯定類型,綁定對象

  二者的區別在於,動態編譯能夠最大程度地支持多態,而多態最大的意義在於下降類的耦合性,所以反射的優勢就很明顯了:解耦以及提升代碼的靈活性。spring

  所以,反射的優點和劣勢分別在於:sql

  • 優點數據庫

    • 運行期類型的判斷,動態類加載:提升代碼靈活度
  • 劣勢設計模式

    • 性能瓶頸:反射至關於一系列解釋操做,通知 JVM 要作的事情,性能比直接的java代碼要慢不少

反射的應用場景

  在咱們平時的項目開發過程當中,基本上不多會直接使用到反射機制,但這不能說明反射機制沒有用,實際上有不少設計、開發都與反射機制有關,例如模塊化的開發,經過反射去調用對應的字節碼;動態代理設計模式也採用了反射機制,還有咱們平常使用的 Spring/Hibernate 等框架,也是利用CGLIB 反射機制才得以實現,下面就舉例最多見的兩個例子,來講明反射機制的強大之處:框架

  • JDBC 的數據庫的鏈接

在JDBC 的操做中,若是要想進行數據庫的鏈接,則必須按照以上的幾步完成模塊化

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

  在 Java的反射機制在作基礎框架的時候很是有用,行內有一句這樣的老話:反射機制是Java框架的基石。通常應用層面不多用,不過這種東西,如今不少開源框架基本都已經封裝好了,本身基本用不着寫。典型的除了hibernate以外,還有spring也用到不少反射機制。最經典的就是xml的配置模式。性能

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

  1. 將程序內全部 XML 或 Properties 配置文件加載入內存中
  2. Java類裏面解析xml或properties裏面的內容,獲得對應實體類的字節碼字符串以及相關的屬性信息
  3. 使用反射機制,根據這個字符串得到某個類的Class實例
  4. 動態配置實例的屬性

Spring這樣作的好處是:

  • 不用每一次都要在代碼裏面去new或者作其餘的事情
  • 之後要改的話直接改配置文件,代碼維護起來就很方便了
  • 有時爲了適應某些需求,Java類裏面不必定能直接調用另外的方法,能夠經過反射機制來實現

模擬 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();
                            //設置值的方法
                            Method mSet = null;
                            //建立一個對象
                            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的反射極致調用對象的某個set方法,並將值設置進去
                                                 mSet.invoke(obj, value);
                                          }
                                   }
                            }
                           
                            //將對象放入beanMap中,其中key爲id值,value爲對象
                            beanMap.put(id.getText(), obj);
                     }
              } catch (Exception e) {
                     System.out.println(e.toString());
              }
       }
      
       //other codes
}
相關文章
相關標籤/搜索