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