近期在維護公司項目的時候遇到一個問題,由於實體類中的 set 方法涉及到了業務邏輯,所以在給對象賦值的過程當中不可以使用 set 方法,爲了實現功能,因此採用了反射的機制給對象屬性賦值,藉此機會也瞭解了反射的一些具體用法和使用場景,分如下兩點對反射進行分析:java
我的理解,反射機制實際上就是上帝模式,若是說方法的調用是 Java 正確的打開方式,那反射機制就是上帝偷偷開的後門,只要存在對應的class,一切都可以被調用。node
那上帝爲何要打開這個後門呢?這涉及到了靜態和動態的概念mysql
二者的區別在於,動態編譯能夠最大程度地支持多態,而多態最大的意義在於下降類的耦合性,所以反射的優勢就很明顯了:解耦以及提升代碼的靈活性。spring
所以,反射的優點和劣勢分別在於:sql
優點數據庫
劣勢設計模式
在咱們平時的項目開發過程當中,基本上不多會直接使用到反射機制,但這不能說明反射機制沒有用,實際上有不少設計、開發都與反射機制有關,例如模塊化的開發,經過反射去調用對應的字節碼;動態代理設計模式也採用了反射機制,還有咱們平常使用的 Spring/Hibernate 等框架,也是利用CGLIB 反射機制才得以實現,下面就舉例最多見的兩個例子,來講明反射機制的強大之處:框架
在JDBC 的操做中,若是要想進行數據庫的鏈接,則必須按照以上的幾步完成模塊化
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(); // 三、關閉數據庫 }
在 Java的反射機制在作基礎框架的時候很是有用,行內有一句這樣的老話:反射機制是Java框架的基石。通常應用層面不多用,不過這種東西,如今不少開源框架基本都已經封裝好了,本身基本用不着寫。典型的除了hibernate以外,還有spring也用到不少反射機制。最經典的就是xml的配置模式。性能
Spring 經過 XML 配置模式裝載 Bean 的過程:
Spring這樣作的好處是:
模擬 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 }