学习spring之前必学之反射技术(IOC)(一)
引述要學習Spring框架的技術內幕,必須事先掌握一些基本的Java知識,正所謂“登高必自卑,涉遠必自邇”。
以下幾項Java知識和Spring框架息息相關,不可不學
??? Java語言允許通過程序化的方式間接對Class進行操作,Class文件由類裝載器裝載后,在JVM中將形成一份
描述Class結構的元信息對象,通過該元信息對象可以獲知Class的結構信息:如構造函數、屬性和方法等。Java
允許用戶借由這個Class相關的元信息對象間接調用Class對象的功能,這就為使用程序化方式操作Class對象開辟
了途徑。
簡單實例
??? 我們將從一個簡單例子開始探訪Java反射機制的征程,下面的Car類擁有兩個構造函數、兩個方法以及三個屬性?
一般情況下,我們會使用如下的代碼創建Car的實例:
或者:
以上兩種方法都采用傳統方式的直接調用目標類的方法,下面我們通過Java反射機制以一種更加通用的方式間接地操作目標類:
運行以上程序,在控制臺上將打印出以下信息:
這說明我們完全可以通過編程方式調用Class的各項功能,這和直接通過構造函數和方法調用類功能的效果是一致的,
只不過前者是間接調用,后者是直接調用罷了。
在ReflectTest中,使用了幾個重要的反射類,分別是ClassLoader、Class、Constructor和Method,通過這些
反射類就可以間接調用目標Class的各項功能了。在①處,我們獲取當前線程的ClassLoader,然后通過指定的全
限定類“com.baobaotao.beans.Car”裝載Car類對應的反射實例。在②處,我們通過Car的反射類對象獲取Car的
構造函數對象cons,通過構造函數對象的newInstrance()方法實例化Car對象,其效果等同于new Car()。在③處
我們又通過Car的反射類對象的getMethod(String methodName,Class paramClass)獲取屬性的Setter方法
對象,第一個參數是目標Class的方法名;第二個參數是方法入參的對象類型。獲取方法反射對象后,即可通過
invoke(Object obj,Object param)方法調用目標類的方法,該方法的第一個參數是操作的目標類對象實例;
第二個參數是目標方法的入參。?
類裝載器ClassLoader
類裝載器工作機制
類裝載器就是尋找類的節碼文件并構造出類在JVM內部表示對象的組件。在Java中,類裝載器把一個類裝入JVM中,要經過以下步驟:
[1.]裝載:查找和導入Class文件;
[2.]鏈接:執行校驗、準備和解析步驟,其中解析步驟是可以選擇的:
? [2.1]校驗:檢查載入Class文件數據的正確性;?
? [2.2]準備:給類的靜態變量分配存儲空間;
? [2.3]解析:將符號引用轉成直接引用;
[3.]初始化:對類的靜態變量、靜態代碼塊執行初始化工作。
?
? ? ? 類裝載工作由ClassLoader及其子類負責,ClassLoader是一個重要的Java運行時系統組件,它負責在運行時
查找和裝入Class字節碼文件。JVM在運行時會產生三個ClassLoader:根裝載器、ExtClassLoader(擴展類裝載器)
和AppClassLoader(系統類裝載器)。其中,根裝載器不是ClassLoader的子類,它使用C++編寫,因此我們在
Java中看不到它,根裝載器負責裝載JRE的核心類庫,如JRE目標下的rt.jar、charsets.jar等。ExtClassLoader
和AppClassLoader都是ClassLoader的子類。其中ExtClassLoader負責裝載JRE擴展目錄ext中的JAR類包;
AppClassLoader負責裝載Classpath路徑下的類包。
這三個類裝載器之間存在父子層級關系,即根裝載器是ExtClassLoader的父裝載器,ExtClassLoader是AppClassLoader的父裝載器。
默認情況下,使用AppClassLoader裝載應用程序的類,我們可以做一個實驗:
運行以上代碼,在控制臺上將打出以下信息:
parent loader:sun.misc.Launcher$ExtClassLoader@15601ea
???? //①根裝載器在Java中訪問不到,所以返回null
grandparent loader:null
通過以上的輸出信息,我們知道當前的ClassLoader是AppClassLoader,父ClassLoader是ExtClassLoader
,祖父ClassLoader是根類裝載器,因為在Java中無法獲得它的句柄,所以僅返回null。
JVM裝載類時使用“全盤負責委托機制”,“全盤負責”是指當一個ClassLoader裝載一個類的時,除非顯式地使用
另一個ClassLoader,該類所依賴及引用的類也由這個ClassLoader載入;“委托機制”是指先委托父裝載器尋找
目標類,只有在找不到的情況下才從自己的類路徑中查找并裝載目標類。這一點是從安全角度考慮的,試想如果
有人編寫了一個惡意的基礎類(如java.lang.String)并裝載到JVM中將會引起多么可怕的后果。但是由于有了
“全盤負責委托機制”,java.lang.String永遠是由根裝載器來裝載的,這樣就避免了上述事件的發生。
ClassLoader重要方法
在Java中,ClassLoader是一個抽象類,位于java.lang包中。下面對該類的一些重要接口方法進行介紹:
- ? Class loadClass(String name)? ?name參數指定類裝載器需要裝載類的名字,必須使用全限定類名,如com.baobaotao. beans.Car。該方法有一個重載方法loadClass(String name ,boolean resolve),resolve參數告訴類裝載器是否需要解析該類。在初始化類之前,應考慮進行類解析的工作,但并不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要進行解析。
- Class defineClass(String name, byte[] b, int off, int len)?將類文件的字節數組轉換成JVM內部的java.lang.Class對象。字節數組可以從本地文件系統、遠程網絡獲取。name為字節數組對應的全限定類名。
- ? Class findSystemClass(String name)? 從本地文件系統載入Class文件,如果本地文件系統不存在該Class文件,將拋出ClassNotFoundException異常。該方法是JVM默認使用的裝載機制。
- ? Class findLoadedClass(String name)?調用該方法來查看ClassLoader是否已裝入某個類。如果已裝入,那么返回java.lang.Class對象,否則返回null。如果強行裝載已存在的類,將會拋出鏈接錯誤。
- ? ClassLoader getParent()?獲取類裝載器的父裝載器,除根裝載器外,所有的類裝載器都有且僅有一個父裝載器,ExtClassLoader的父裝載器是根裝載器,因為根裝載器非Java編寫,所以無法獲得,將返回null。
除JVM默認的三個ClassLoader以外,可以編寫自己的第三方類裝載器,以實現一些特殊的需求。類文件被裝載并解析后,
在JVM內將擁有一個對應的java.lang.Class類描述對象,該類的實例都擁有指向這個類描述對象的引用,而類描述對象
又擁有指向關聯ClassLoader的引用,如圖3-4所示。
每一個類在JVM中都擁有一個對應的java.lang.Class對象,它提供了類結構信息的描述。數組、
枚舉、注解以及基本Java類型(如int、double等),甚至void都擁有對應的Class對象。Class
沒有public的構造方法。Class對象是在裝載類時由JVM通過調用類裝載器中的defineClass()方
法自動構造的。
Java反射機制
Class反射對象描述類語義結構,可以從Class對象中獲取構造函數、成員變量、方法類等類元素的
反射對象,并以編程的方式通過這些反射對象對目標類對象進行操作。這些反射對象類在java.reflect
包中定義,下面是最主要的三個反射類:?
- ?? Constructor:類的構造函數反射類,通過Class#getConstructors()方法可以獲得類的所有構造函數反射對象數組。在JDK5.0中,還可以通過getConstructor(Class... parameterTypes)獲取擁有特定入參的構造函數反射對象。Constructor的一個主要方法是newInstance(Object[] initargs),通過該方法可以創建一個對象類的實例,相當于new關鍵字。在JDK5.0中該方法演化為更為靈活的形式:newInstance (Object... initargs)。
- ?? Method:類方法的反射類,通過Class#getDeclaredMethods()方法可以獲取類的所有方法反射類對象數組Method[]。在JDK5.0中可以通過getDeclaredMethod(String name, Class... parameterTypes)獲取特定簽名的方法,name為方法名;Class...為方法入參類型列表。Method最主要的方法是invoke(Object obj, Object[] args),obj表示操作的目標對象;args為方法入參,代碼清單3 10③處演示了這個反射類的使用方法。在JDK 5.0中,該方法的形式調整為invoke(Object obj, Object... args)。此外,Method還有很多用于獲取類方法更多信息的方法: ????? 1)Class getReturnType():獲取方法的返回值類型;
- ????? 2)Class[] getParameterTypes():獲取方法的入參類型數組;
- ????? 3)Class[] getExceptionTypes():獲取方法的異常類型數組;
- ????? 4)Annotation[][] getParameterAnnotations():獲取方法的注解信息,JDK 5.0中的新方法;
- ?? Field:類的成員變量的反射類,通過Class#getDeclaredFields()方法可以獲取類的成員變量反射對象數組,通過Class#getDeclaredField(String name)則可獲取某個特定名稱的成員變量反射對象。Field類最主要的方法是set(Object obj, Object value),obj表示操作的目標對象,通過value為目標對象的成員變量設置值。如果成員變量為基礎類型,用戶可以使用Field類中提供的帶類型名的值設置方法,如setBoolean(Object obj, boolean value)、setInt(Object obj, int value)等。
此外,Java還為包提供了Package反射類,在JDK 5.0中還為注解提供了AnnotatedElement反射類。總之,
Java的反射體系保證了可以通過程序化的方式訪問目標類中所有的元素,對于private或protected的成員變量
和方法,只要JVM的安全機制允許,也可以通過反射進行調用,請看下面的例子:
Java代碼 ?
color變量和drive()方法都是私有的,通過類實例變量無法在外部訪問私有變量、調用私有方法的,但通過
反射機制卻可以繞過這個限制:
運行該類,打印出以下信息:
在訪問private、protected成員變量和方法時必須通過setAccessible(boolean access)方法取消Java語言檢查,
否則將拋出IllegalAccessException。如果JVM的安全管理器設置了相應的安全機制,調用該方法將拋出SecurityException。
轉載于:https://www.cnblogs.com/fyboke/p/6428692.html
總結
以上是生活随笔為你收集整理的学习spring之前必学之反射技术(IOC)(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#使用Gecko实现浏览器
- 下一篇: win7下nsis打包exe安装程序教程