Spring IOC AOP的原理 若是讓你本身設計IOC,AOP如何處理(百度)

百度的面試官問,若是讓你本身設計一個IOC,和AOP,如何設計,html

我把IOC的過程答出來了,可是明顯不對, 
(1) IOC 利用了反射,本身有個id,classtype,hashmap,全部的功能都在hashmap中,而後利用反射的Class.forName把把classtype轉化成類,而後利用反射的setFieldValue()從hashMap中把屬性和方法取出來,注入進去。最終把類建立出來,java

(2)AOP是動態代理,其實底層也是反射;面試

1、如何本身實現Spring的IOC的功能

咱們都知道,IOC是利用了反射機制,接下來就讓咱們本身寫個Spring 來看看Spring 究竟是怎麼運行的吧! (百度二面:如何本身實現Spring的 IOC依賴反轉的功能)
首先,咱們定義一個Bean類,這個類用來存放一個Bean擁有的屬性 spring

/* Bean Id */  
    private String id;  
    /* Bean Class */  
    private String type; //這是類名稱 /* Bean Property */ private Map<String, Object> properties = new HashMap<String, Object>(); 

 

 一個Bean包括id,type,和Properties。 type 就是Class類名稱數據庫

<bean id="personService" class="cn.itcast.service.OrderFactory" factory-method="createOrder"/>

public class OrderFactory {
    // 注意這裏的這個方法是 static 的!
    public static OrderServiceBean createOrder(){ return new OrderServiceBean(); } }

 

接下來Spring 就開始加載咱們的配置文件了,將咱們配置的信息保存在一個HashMap中,HashMap的key就是Bean 的 Id ,HasMap 的value是這個Bean,只有這樣咱們才能經過context.getBean("animal")這個方法得到Animal這個類。咱們都知道Spirng能夠注入基本類型,並且能夠注入像List,Map這樣的類型,接下來就讓咱們以Map爲例看看Spring是怎麼保存的吧 編程

Map配置能夠像下面的   緩存

<bean id="test" class="Test">  
        <property name="testMap">  
            <map>  
                <entry key="a">  
                    <value>1</value>  
                </entry>  
                <entry key="b">  
                    <value>2</value>  
                </entry>  
            </map>  
        </property>  
    </bean> 

 

 Spring是怎樣保存上面的配置呢?,代碼以下:  性能優化

if (beanProperty.element("map") != null) {  
                    Map<String, Object> propertiesMap = new HashMap<String, Object>(); Element propertiesListMap = (Element) beanProperty .elements().get(0); Iterator<?> propertiesIterator = propertiesListMap .elements().iterator(); while (propertiesIterator.hasNext()) { Element vet = (Element) propertiesIterator.next(); if (vet.getName().equals("entry")) { String key = vet.attributeValue("key"); Iterator<?> valuesIterator = vet.elements() .iterator(); while (valuesIterator.hasNext()) { Element value = (Element) valuesIterator.next(); if (value.getName().equals("value")) { propertiesMap.put(key, value.getText()); } if (value.getName().equals("ref")) { propertiesMap.put(key, new String[] { value .attributeValue("bean") }); } } } } bean.getProperties().put(name, propertiesMap); }

 

 接下來就進入最核心部分了,讓咱們看看Spring 究竟是怎麼依賴注入的吧,其實依賴注入的思想也很簡單,它是經過反射機制實現的,在實例化一個類時,它經過反射調用類中set方法將事先保存在HashMap中的類屬性注入到類中。讓咱們看看具體它是怎麼作的吧。 app

首先實例化一個類,像這樣  框架

public static Object newInstance(String className) {  
        Class<?> cls = null; Object obj = null; try { cls = Class.forName(className); obj = cls.newInstance(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } return obj; } 

 

 接着它將這個類的依賴注入進去,像這樣   

public static void setProperty(Object obj, String name, String value) {  
        Class<? extends Object> clazz = obj.getClass(); try { String methodName = returnSetMthodName(name); Method[] ms = clazz.getMethods(); for (Method m : ms) { if (m.getName().equals(methodName)) { if (m.getParameterTypes().length == 1) { Class<?> clazzParameterType = m.getParameterTypes()[0]; setFieldValue(clazzParameterType.getName(), value, m, obj); break; } } } } catch (SecurityException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } 

 

最後它將這個類的實例返回給咱們,咱們就能夠用了。咱們仍是以Map爲例看看它是怎麼作的,我寫的代碼裏面是建立一個HashMap並把該HashMap注入到須要注入的類中,像這樣, 

if (value instanceof Map) {  
                Iterator<?> entryIterator = ((Map<?, ?>) value).entrySet() .iterator(); Map<String, Object> map = new HashMap<String, Object>(); while (entryIterator.hasNext()) { Entry<?, ?> entryMap = (Entry<?, ?>) entryIterator.next(); if (entryMap.getValue() instanceof String[]) { map.put((String) entryMap.getKey(), getBean(((String[]) entryMap.getValue())[0])); } } BeanProcesser.setProperty(obj, property, map); } 

 

好了,這樣咱們就能夠用Spring 給咱們建立的類了,是否是也不是很難啊?固然Spring能作到的遠不止這些,這個示例程序僅僅提供了Spring最核心的依賴注入功能中的一部分。  

 參考:Spring IOC 原理

2、IOC原理

讓咱們來看下IoC容器究竟是如何工做。在此咱們以xml配置方式來分析一下:

1、準備配置文件:就像前邊Hello World配置文件同樣,在配置文件中聲明Bean定義也就是爲Bean配置元數據。

2、由IoC容器進行解析元數據: IoC容器的Bean Reader讀取並解析配置文件,根據定義生成BeanDefinition配置元數據對象,IoC容器根據BeanDefinition進行實例化、配置及組裝Bean。

3、實例化IoC容器:由客戶端實例化容器,獲取須要的Bean。 

實例化bean 有三種方式:類構造器實例化、靜態工廠方法實例化及實例工廠方法實例化

參考:Spring實例化Bean的三種方式及Bean的類型

IoC(Inversion of Control)  

  (1). IoC(Inversion of Control)是指容器控制程序對象之間的關係,而不是傳統實現中,由程序代碼直接操控。控制權由應用代碼中轉到了外部容器,控制權的轉移是所謂反轉。 對於Spring而言,就是由Spring來控制對象的生命週期和對象之間的關係;IoC還有另一個名字——「依賴注入(Dependency Injection)」。從名字上理解,所謂依賴注入,即組件之間的依賴關係由容器在運行期決定,即由容器動態地將某種依賴關係注入到組件之中。  

(2). 在Spring的工做方式中,全部的類都會在spring容器中登記,告訴spring這是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。

(3). 在系統運行中,動態的向某個對象提供它所須要的其餘對象。  

(4). 依賴注入的思想是經過反射機制實現的,在實例化一個類時,它經過反射調用類中set方法將事先保存在HashMap中的類屬性(至因而什麼樣的HashMap後面會提到)注入到類中。 總而言之,在傳統的對象建立方式中,一般由調用者來建立被調用者的實例,而在Spring中建立被調用者的工做由Spring來完成,而後注入調用者,即所謂的依賴注入or控制反轉。 注入方式有兩種:依賴注入和設置注入; IoC的優勢:下降了組件之間的耦合,下降了業務對象之間替換的複雜性,使之可以靈活的管理對象。

 

控制反轉/依賴注入

IOC(DI):Java若是對象調用對象,對象間的耦合度高了。而IOC的思想是:Spring容器來實現這些相互依賴對象的建立、協調工做。對象只須要關係業務邏輯自己就能夠了。從這方面來講,對象如何獲得他的協做對象的責任被反轉了(IOC、DI)。DI其實就是IOC的另一種說法。DI 就是:得到依賴對象的方式反轉了。 

IoC與DI

  首先想說說IoC(Inversion of Control,控制倒轉)所謂IoC,對於spring框架來講,就是由spring來負責控制對象的生命週期和對象間的關係。這是什麼意思呢,舉個簡單的例子,咱們是如何找女友的?常見的狀況是,咱們處處去看哪裏有長得漂亮身材又好的mm,而後打聽她們的興趣愛好、qq號、電話號、ip號、iq號………,想辦法認識她們,投其所好送其所要,而後嘿嘿……這個過程是複雜深奧的,咱們必須本身設計和麪對每一個環節。傳統的程序開發也是如此,在一個對象中,若是要使用另外的對象,就必須獲得它(本身new一個,或者從JNDI中查詢一個),使用完以後還要將對象銷燬(好比Connection等),對象始終會和其餘的接口或類藕合起來。

 那麼IoC是如何作的呢?有點像經過婚介找女友,在我和女友之間引入了一個第三者:婚姻介紹所。婚介管理了不少男男女女的資料,我能夠向婚介提出一個列表,告訴它我想找個什麼樣的女友,好比長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,而後婚介就會按照咱們的要求,提供一個mm,咱們只須要去和她談戀愛、結婚就好了。簡單明瞭,若是婚介給咱們的人選不符合要求,咱們就會拋出異常。整個過程再也不由我本身控制,而是有婚介這樣一個相似容器的機構來控制。Spring所倡導的開發方式就是如此,全部的類都會在spring容器中登記,告訴spring你是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象,而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。若是你還不明白的話,我決定放棄。

IoC的一個重點是在系統運行中,動態的向某個對象提供它所須要的其餘對象。這一點是經過DI(Dependency Injection,依賴注入)來實現的。好比對象A須要操做數據庫,之前咱們老是要在A中本身編寫代碼來得到一個Connection對象,有了 spring咱們就只須要告訴spring,A中須要一個Connection,至於這個Connection怎麼構造,什麼時候構造,A不須要知道。在系統運行時,spring會在適當的時候製造一個Connection,而後像打針同樣,注射到A當中,這樣就完成了對各個對象之間關係的控制。A須要依賴 Connection才能正常運行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? Java 1.3以後一個重要特徵是反射(reflection),它容許程序在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,spring就是經過反射來實現注入的。關於反射的相關資料請查閱java doc。

 理解了IoC和DI的概念後,一切都將變得簡單明瞭,剩下的工做只是在spring的框架中堆積木而已。

下面來讓你們瞭解一下Spring究竟是怎麼運行的。  

public static void main(String[] args) {  
        ApplicationContext context = new FileSystemXmlApplicationContext(  
                "applicationContext.xml");  
        Animal animal = (Animal) context.getBean("animal");  
        animal.say();  
    }  

 這段代碼你必定很熟悉吧,不過仍是讓咱們分析一下它吧,首先是applicationContext.xml 

<bean id="animal" class="phz.springframework.test.Cat">  
        <property name="name" value="kitty" />  
    </bean> 

  他有一個類phz.springframework.test.Cat   

public class Cat implements Animal {  
    private String name;  
    public void say() {  
        System.out.println("I am " + name + "!");  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  

 實現了phz.springframework.test.Animal接口  

public interface Animal {  
    public void say();  
}  

 

很明顯上面的代碼輸出I am kitty! 那麼到底Spring是如何作到的呢? 

 

 

AOP(Aspect Oriented Programming)

(1). AOP面向方面編程基於IoC,是對OOP的有益補充;

(2). AOP利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了 多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即方面。所謂「方面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的 邏輯或責任封裝起來,好比日誌記錄,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。

(3). AOP表明的是一個橫向的關 系,將「對象」比做一個空心的圓柱體,其中封裝的是對象的屬性和行爲;則面向方面編程的方法,就是將這個圓柱體以切面形式剖開,選擇性的提供業務邏輯。而 剖開的切面,也就是所謂的「方面」了。而後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡,但完成了效果。

(4). 實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼。

(5). Spring實現AOP:JDK動態代理和CGLIB代理 JDK動態代理:其代理對象必須是某個接口的實現,它是經過在運行期間建立一個接口的實現類來完成對目標對象的代理;其核心的兩個類是InvocationHandler和Proxy。 CGLIB代理:實現原理相似於JDK動態代理,只是它在運行期間生成的代理對象是針對目標類擴展的子類。CGLIB是高效的代碼生成包,底層是依靠ASM(開源的java字節碼編輯類庫)操做字節碼實現的,性能比JDK強;須要引入包asm.jar和cglib.jar。     使用AspectJ注入式切面和@AspectJ註解驅動的切面實際上底層也是經過動態代理實現的。

若是須要了解具體的動態代理參考:深刻理解Java反射+動態代理

(6). AOP使用場景:                     

Authentication 權限檢查        

Caching 緩存        

Context passing 內容傳遞        

Error handling 錯誤處理        

Lazy loading 延遲加載        

Debugging  調試      

logging, tracing, profiling and monitoring 日誌記錄,跟蹤,優化,校準        

Performance optimization 性能優化,效率檢查        

Persistence  持久化        

Resource pooling 資源池        

Synchronization 同步        

Transactions 事務管理    

另外Filter的實現和struts2的攔截器的實現都是AOP思想的體現。  

參考:Spring框架IOC和AOP的實現原理

相關文章
相關標籤/搜索