反射機制概念
我們考慮一個場景,如果我們在程序運行時,一個對象想要檢視自己所擁有的成員屬性,該如何操作?再考慮另一個場景,如果我們想要在運行期獲得某個類的Class信息如它的屬性、構造方法、一般方法后再考慮是否創建它的對象,這種情況該怎么辦呢?這就需要用到反射!
我們.java文件在編譯后會變成.class文件,這就像是個鏡面,本身是.java,在鏡中是.class,他們其實是一樣的;那么同理,我們看到鏡子的反射是.class,就能通過反編譯,了解到.java文件的本來面目。
對於反射,官方給出的概念:反射是Java語言的一個特性,它允許程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個Java類獲取它所有的成員變量和方法並且顯示出來。
反射主要是指程序可以訪問,檢測和修改它本身狀態或行為的一種能力,並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。在Java中,只要給定類的名字,那么就可以通過反射機制來獲得類的所有信息。
反射是Java中一種強大的工具,能夠使我們很方便的創建靈活的代碼,這些代碼可以再運行時裝配,無需在組件之間進行源代碼鏈接。但是反射使用不當會成本很高!類中有什么信息,利用反射機制就能可以獲得什么信息,不過前提是得知道類的名字。
反射機制的作用
1、在運行時判斷任意一個對象所屬的類;
2、在運行時獲取類的對象;
3、在運行時訪問java對象的屬性,方法,構造方法等。
首先要搞清楚為什么要用反射機制?直接創建對象不就可以了嗎,這就涉及到了動態與靜態的概念。
靜態編譯:在編譯時確定類型,綁定對象,即通過。
動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了Java的靈活性,體現了多態的應用,有以降低類之間的藕合性。
反射機制的優缺點
反射機制的優點:可以實現動態創建對象和編譯,體現出很大的靈活性(特別是在J2EE的開發中它的靈活性就表現的十分明顯)。通過反射機制我們可以獲得類的各種內容,進行反編譯。對於JAVA這種先編譯再運行的語言來說,反射機制可以使代碼更加靈活,更加容易實現面向對象。
比如,一個大型的軟件,不可能一次就把把它設計得很完美,把這個程序編譯后,發布了,當發現需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個軟件肯定是沒有多少人用的。采用靜態的話,需要把整個程序重新編譯一次才可以實現功能的更新,而采用反射機制的話,它就可以不用卸載,只需要在運行時動態地創建和編譯,就可以實現該功能。
反射機制的缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么並且讓它滿足我們的要求。這類操作總是慢於直接執行相同的操作。
反射與工廠模式實現IOC
Spring中的IoC的實現原理就是工廠模式加反射機制。 我們首先看一下不用反射機制時的工廠模式:
- interface fruit{
- public abstract void eat();
- }
- class Apple implements fruit{
- public void eat(){
- System.out.println("Apple");
- }
- }
- class Orange implements fruit{
- public void eat(){
- System.out.println("Orange");
- }
- }
- //構造工廠類
- //也就是說以后如果我們在添加其他的實例的時候只需要修改工廠類就行了
- class Factory{
- public static fruit getInstance(String fruitName){
- fruit f=null;
- if("Apple".equals(fruitName)){
- f=new Apple();
- }
- if("Orange".equals(fruitName)){
- f=new Orange();
- }
- return f;
- }
- }
- class hello{
- public static void main(String[] a){
- fruit f=Factory.getInstance("Orange");
- f.eat();
- }
- }
上面寫法的缺點是當我們再添加一個子類的時候,就需要修改工廠類了。如果我們添加太多的子類的時候,改動就會很多。下面用反射機制實現工廠模式:
- interface fruit{
- public abstract void eat();
- }
- class Apple implements fruit{
- public void eat(){
- System.out.println("Apple");
- }
- }
- class Orange implements fruit{
- public void eat(){
- System.out.println("Orange");
- }
- }
- class Factory{
- public static fruit getInstance(String ClassName){
- fruit f=null;
- try{
- f=(fruit)Class.forName(ClassName).newInstance();
- }catch (Exception e) {
- e.printStackTrace();
- }
- return f;
- }
- }
- class hello{
- public static void main(String[] a){
- fruit f=Factory.getInstance("Reflect.Apple");
- if(f!=null){
- f.eat();
- }
- }
- }
現在就算我們添加任意多個子類的時候,工廠類都不需要修改。使用反射機制實現的工廠模式可以通過反射取得接口的實例,但是需要傳入完整的包和類名。而且用戶也無法知道一個接口有多少個可以使用的子類,所以我們通過屬性文件的形式配置所需要的子類。
下面編寫使用反射機制並結合屬性文件的工廠模式(即IoC)。首先創建一個fruit.properties的資源文件:
- apple=Reflect.Apple
- orange=Reflect.Orange
然后編寫主類代碼:
- interface fruit{
- public abstract void eat();
- }
- class Apple implements fruit{
- public void eat(){
- System.out.println("Apple");
- }
- }
- class Orange implements fruit{
- public void eat(){
- System.out.println("Orange");
- }
- }
- //操作屬性文件類
- class init{
- public static Properties getPro() throws FileNotFoundException, IOException{
- Properties pro=new Properties();
- File f=new File("fruit.properties");
- if(f.exists()){
- pro.load(new FileInputStream(f));
- }else{
- pro.setProperty("apple", "Reflect.Apple");
- pro.setProperty("orange", "Reflect.Orange");
- pro.store(new FileOutputStream(f), "FRUIT CLASS");
- }
- return pro;
- }
- }
- class Factory{
- public static fruit getInstance(String ClassName){
- fruit f=null;
- try{
- f=(fruit)Class.forName(ClassName).newInstance();
- }catch (Exception e) {
- e.printStackTrace();
- }
- return f;
- }
- }
- class hello{
- public static void main(String[] a) throws FileNotFoundException, IOException{
- Properties pro=init.getPro();
- fruit f=Factory.getInstance(pro.getProperty("apple"));
- if(f!=null){
- f.eat();
- }
- }
- }
運行結果:Apple
IOC容器的技術剖析
IOC中最基本的技術就是“反射(Reflection)”編程,通俗來講就是根據給出的類名(字符串方式)來動態地生成對象,這種編程方式可以讓對象在生成時才被決定到底是哪一種對象。只是在Spring中要生產的對象都在配置文件中給出定義,目的就是提高靈活性和可維護性。
目前C#、Java和PHP5等語言均支持反射,其中PHP5的技術書籍中,有時候也被翻譯成“映射”。有關反射的概念和用法,大家應該都很清楚。反射的應用是很廣泛的,很多的成熟的框架,比如像Java中的Hibernate、Spring框架,.Net中NHibernate、Spring.NET框架都是把”反射“做為最基本的技術手段。
反射技術其實很早就出現了,但一直被忽略,沒有被進一步的利用。當時的反射編程方式相對於正常的對象生成方式要慢至少得10倍。現在的反射技術經過改良優化,已經非常成熟,反射方式生成對象和通常對象生成方式,速度已經相差不大了,大約為1-2倍的差距。
我們可以把IOC容器的工作模式看做是工廠模式的升華,可以把IOC容器看作是一個工廠,這個工廠里要生產的對象都在配置文件中給出定義,然后利用編程語言提供的反射機制,根據配置文件中給出的類名生成相應的對象。從實現來看,IOC是把以前在工廠方法里寫死的對象生成代碼,改變為由配置文件來定義,也就是把工廠和對象生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性。
使用IOC框架應該注意什么
使用IOC框架產品能夠給我們的開發過程帶來很大的好處,但是也要充分認識引入IOC框架的缺點,做到心中有數,杜絕濫用框架。
1)軟件系統中由於引入了第三方IOC容器,生成對象的步驟變得有些復雜,本來是兩者之間的事情,又憑空多出一道手續,所以,我們在剛開始使用IOC框架的時候,會感覺系統變得不太直觀。所以,引入了一個全新的框架,就會增加團隊成員學習和認識的培訓成本,並且在以后的運行維護中,還得讓新加入者具備同樣的知識體系。
2)由於IOC容器生成對象是通過反射方式,在運行效率上有一定的損耗。如果你要追求運行效率的話,就必須對此進行權衡。
3)具體到IOC框架產品(比如Spring)來講,需要進行大量的配制工作,比較繁瑣,對於一些小的項目而言,客觀上也可能加大一些工作成本。
4)IOC框架產品本身的成熟度需要進行評估,如果引入一個不成熟的IOC框架產品,那么會影響到整個項目,所以這也是一個隱性的風險。
我們大體可以得出這樣的結論:一些工作量不大的項目或者產品,不太適合使用IOC框架產品。另外,如果團隊成員的知識能力欠缺,對於IOC框架產品缺乏深入的理解,也不要貿然引入。最后,特別強調運行效率的項目或者產品,也不太適合引入IOC框架產品,像WEB2.0網站就是這種情況。