面試官:Java 反射機制的應用場景?
點擊關(guān)注公眾號,Java干貨及時送達??

作者:Seven_Nee
來源:https://segmentfault.com/a/1190000010162647
近期在維護公司項目的時候遇到一個問題,因為實體類中的 set 方法涉及到了業(yè)務邏輯,因此在給對象賦值的過程中不能夠使用 set 方法,為了實現(xiàn)功能,所以采用了反射的機制給對象屬性賦值,借此機會也了解了反射的一些具體用法和使用場景,分以下兩點對反射進行分析:
反射的優(yōu)勢和劣勢 反射的應用場景
反射的優(yōu)勢和劣勢
個人理解,反射機制實際上就是上帝模式,如果說方法的調(diào)用是 Java 正確的打開方式,那反射機制就是上帝偷偷開的后門,只要存在對應的class,一切都能夠被調(diào)用。
那上帝為什么要打開這個后門呢?
這涉及到了靜態(tài)和動態(tài)的概念:
靜態(tài)編譯:在編譯時確定類型,綁定對象 動態(tài)編譯:運行時確定類型,綁定對象
兩者的區(qū)別在于,動態(tài)編譯可以最大程度地支持多態(tài),而多態(tài)最大的意義在于降低類的耦合性,因此反射的優(yōu)點就很明顯了:解耦以及提高代碼的靈活性。
因此,反射的優(yōu)勢和劣勢分別在于:
優(yōu)勢
運行期類型的判斷,動態(tài)類加載:提高代碼靈活度
劣勢
性能瓶頸:反射相當于一系列解釋操作,通知 JVM 要做的事情,性能比直接的java代碼要慢很多。
反射的應用場景
在我們平時的項目開發(fā)過程中,基本上很少會直接使用到反射機制,但這不能說明反射機制沒有用,實際上有很多設計、開發(fā)都與反射機制有關(guān),例如模塊化的開發(fā),通過反射去調(diào)用對應的字節(jié)碼。
動態(tài)代理設計模式也采用了反射機制,還有我們?nèi)粘J褂玫?Spring/Hibernate 等框架,也是利用CGLIB 反射機制才得以實現(xiàn),下面就舉例最常見的兩個例子,來說明反射機制的強大之處。
JDBC 的數(shù)據(jù)庫的連接
在JDBC 的操作中,如果要想進行數(shù)據(jù)庫的連接,則必須按照以上的幾步完成
通過Class.forName()加載數(shù)據(jù)庫的驅(qū)動程序 (通過反射加載,前提是引入相關(guān)了Jar包) 通過 DriverManager 類進行數(shù)據(jù)庫的連接,連接的時候要輸入數(shù)據(jù)庫的連接地址、用戶名、密碼 通過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的反射機制在做基礎框架的時候非常有用,行內(nèi)有一句這樣的老話:反射機制是Java框架的基石。一般應用層面很少用,不過這種東西,現(xiàn)在很多開源框架基本都已經(jīng)封裝好了,自己基本用不著寫。
典型的除了hibernate之外,還有spring也用到很多反射機制。最經(jīng)典的就是xml的配置模式。
Spring 通過 XML 配置模式裝載 Bean 的過程:
將程序內(nèi)所有 XML 或 Properties 配置文件加載入內(nèi)存中 Java類里面解析xml或properties里面的內(nèi)容,得到對應實體類的字節(jié)碼字符串以及相關(guān)的屬性信息 使用反射機制,根據(jù)這個字符串獲得某個類的Class實例 動態(tài)配置實例的屬性
Spring這樣做的好處是:
不用每一次都要在代碼里面去new或者做其他的事情 以后要改的話直接改配置文件,代碼維護起來就很方便了 有時為了適應某些需求,Java類里面不一定能直接調(diào)用另外的方法,可以通過反射機制來實現(xiàn)
模擬 Spring 加載 XML 配置文件:
public?class?BeanFactory?{
???????private?Map?beanMap?=?new?HashMap();
???????/**
???????*?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;
????????????????????????????//創(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???????????????????????????????????????????if?(pd[k].getName().equalsIgnoreCase(name.getText()))?{
?????????????????????????????????????????????????mSet?=?pd[k].getWriteMethod();
?????????????????????????????????????????????????//利用Java的反射極致調(diào)用對象的某個set方法,并將值設置進去
?????????????????????????????????????????????????mSet.invoke(obj,?value);
??????????????????????????????????????????}
???????????????????????????????????}
????????????????????????????}
???????????????????????????
????????????????????????????//將對象放入beanMap中,其中key為id值,value為對象
????????????????????????????beanMap.put(id.getText(),?obj);
?????????????????????}
??????????????}?catch?(Exception?e)?{
?????????????????????System.out.println(e.toString());
??????????????}
???????}
??????
???????//other?codes
}
1.?取代MybatisPlus?阿里推出了新 ORM 框架!(兩者對比參考)
3.?新人問一般都用哪些 Linux 命令,我把這個扔了過去
4.?線程與鎖
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點“在看”,關(guān)注公眾號并回復?Java?領取,更多內(nèi)容陸續(xù)奉上。
文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。
謝謝支持喲 (*^__^*)

