面試官:Java 反射機(jī)制的應(yīng)用場景?
點(diǎn)擊關(guān)注公眾號,Java干貨及時(shí)送達(dá)
作者:Seven_Nee
來源:https://segmentfault.com/a/1190000010162647
近期在維護(hù)公司項(xiàng)目的時(shí)候遇到一個(gè)問題,因?yàn)閷?shí)體類中的 set 方法涉及到了業(yè)務(wù)邏輯,因此在給對象賦值的過程中不能夠使用 set 方法,為了實(shí)現(xiàn)功能,所以采用了反射的機(jī)制給對象屬性賦值,借此機(jī)會也了解了反射的一些具體用法和使用場景,分以下兩點(diǎn)對反射進(jìn)行分析:
反射的優(yōu)勢和劣勢 反射的應(yīng)用場景
反射的優(yōu)勢和劣勢
這涉及到了靜態(tài)和動(dòng)態(tài)的概念:
運(yùn)行期類型的判斷,動(dòng)態(tài)類加載:提高代碼靈活度
性能瓶頸:反射相當(dāng)于一系列解釋操作,通知 JVM 要做的事情,性能比直接的java代碼要慢很多。
反射的應(yīng)用場景
在我們平時(shí)的項(xiàng)目開發(fā)過程中,基本上很少會直接使用到反射機(jī)制,但這不能說明反射機(jī)制沒有用,實(shí)際上有很多設(shè)計(jì)、開發(fā)都與反射機(jī)制有關(guān),例如模塊化的開發(fā),通過反射去調(diào)用對應(yīng)的字節(jié)碼。
動(dòng)態(tài)代理設(shè)計(jì)模式也采用了反射機(jī)制,還有我們?nèi)粘J褂玫?Spring/Hibernate 等框架,也是利用CGLIB 反射機(jī)制才得以實(shí)現(xiàn),下面就舉例最常見的兩個(gè)例子,來說明反射機(jī)制的強(qiáng)大之處。
JDBC 的數(shù)據(jù)庫的連接
在JDBC 的操作中,如果要想進(jìn)行數(shù)據(jù)庫的連接,則必須按照以上的幾步完成
通過Class.forName()加載數(shù)據(jù)庫的驅(qū)動(dòng)程序 (通過反射加載,前提是引入相關(guān)了Jar包) 通過 DriverManager 類進(jìn)行數(shù)據(jù)庫的連接,連接的時(shí)候要輸入數(shù)據(jù)庫的連接地址、用戶名、密碼 通過Connection 接口接收連接
public?class?ConnectionJDBC?{??
??
????/**?
?????*?@param?args?
?????*/??
????//驅(qū)動(dòng)程序就是之前在classpath中配置的JDBC的驅(qū)動(dòng)程序的JAR?包中??
????public?static?final?String?DBDRIVER?=?"com.mysql.jdbc.Driver";??
????//連接地址是由各個(gè)數(shù)據(jù)庫生產(chǎn)商單獨(dú)提供的,所以需要單獨(dú)記住??
????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ū)動(dòng)程序?,反射機(jī)制的體現(xiàn)?
????????con?=?DriverManager.getConnection(DBURL,DBUSER,DBPASS);?//2、連接數(shù)據(jù)庫??
????????System.out.println(con);??
????????con.close();?//?3、關(guān)閉數(shù)據(jù)庫??
????}??
Spring 框架的使用
推薦一個(gè) Spring Boot 基礎(chǔ)教程及實(shí)戰(zhàn)示例:https://github.com/javastacks/spring-boot-best-practice
Spring 通過 XML 配置模式裝載 Bean 的過程:
將程序內(nèi)所有 XML 或 Properties 配置文件加載入內(nèi)存中 Java類里面解析xml或properties里面的內(nèi)容,得到對應(yīng)實(shí)體類的字節(jié)碼字符串以及相關(guān)的屬性信息 使用反射機(jī)制,根據(jù)這個(gè)字符串獲得某個(gè)類的Class實(shí)例 動(dòng)態(tài)配置實(shí)例的屬性
Spring這樣做的好處是:
不用每一次都要在代碼里面去new或者做其他的事情 以后要改的話直接改配置文件,代碼維護(hù)起來就很方便了 有時(shí)為了適應(yīng)某些需求,Java類里面不一定能直接調(diào)用另外的方法,可以通過反射機(jī)制來實(shí)現(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反射機(jī)制,通過class的名稱獲取Class對象
Class bean = Class.forName(cls.getText());
//獲取對應(yīng)class的信息
java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
//獲取其屬性描述
java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
//設(shè)置值的方法
Method mSet = null;
//創(chuàng)建一個(gè)對象
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)用對象的某個(gè)set方法,并將值設(shè)置進(jìn)去
mSet.invoke(obj, value);
}
}
}
//將對象放入beanMap中,其中key為id值,value為對象
beanMap.put(id.getText(), obj);
}
} catch (Exception e) {
System.out.println(e.toString());
}
}
//other codes
}
最后,關(guān)注公眾號Java技術(shù)棧,在后臺回復(fù):面試,可以獲取我整理的 Java 系列面試題和答案,非常齊全。







關(guān)注Java技術(shù)棧看更多干貨


