Spring IOC與AOP

1.IOC原理

  IOC(Inversion of Control)控制反轉。html

  伴隨着工業級應用的規模愈來愈龐大,對象之間的依賴關係也愈來愈複雜,常常會出現對象之間的多重依賴性關係,所以,架構師和設計師對於系統的分析和設計,將面臨更大的挑戰。對象之間耦合度太高的系統,必然會出現牽一髮而動全身的情形。java

  IOC即藉助於「第三方」(IOC容器)實現具備依賴關係的對象之間的解耦。spring

  IOC模式,系統中經過引入實現了IOC模式的IOC容器,便可由IOC容器來管理對象的生命週期、依賴關係等,從而使得應用程序的配置和依賴性規範與實際的應用程序代碼分開。其中一個特色就是經過文本的配置文件進行應用程序組件間相互關係的配置,而不用從新修改並編譯具體的代碼。編程

  能夠把IoC模式看作是工廠模式的昇華,能夠把IoC看做是一個大工廠,只不過這個大工廠裏要生成的對象都是在XML文件中給出定義的,而後利用Java 的「反射」編程,根據XML中給出的類名生成相應的對象。從實現來看,IoC是把之前在工廠方法裏寫死的對象生成代碼,改變爲由XML文件來定義,也就是把工廠和對象生成這二者獨立分隔開來,目的就是提升靈活性和可維護性。緩存

  IoC中最基本的Java技術就是「反射」編程。反射又是一個生澀的名詞,通俗的說反射就是 根據給出的類名(字符串)來生成對象。這種編程方式可讓對象在 生成時才決定要生成哪種對象。反射的應用是很普遍的,象Hibernate、Spring中都是用「反射」作爲最基本的技術手段。
  在過去,反射編程方式相對於正常的對象生成方式要慢10幾倍,這也許也是當時爲何反射技術沒有普通應用開來的緣由。但經SUN改良優化後,反射方式生成對象和一般對象生成方式,速度已經相差不大了(但依然有 一倍以上的差距)。

2.Spring IOC實現

  Resource定位過程:使用ClassPathResource來定位,尋找以文件形式存在的BeanDefinition信息。安全

  BeanDefinition的載入:至關於把定義的BeanDefinition在IOC容器中轉化成一個Spring內部表示的數據結構的過程。數據結構

  BeanDefinition的解析:按照Spring的Bean定義規則來對這個XML的文檔樹進行解析。先調用XML的解析器DOM4J進行解析獲得ducument對象,而後才按Spring規則進行解析。架構

  BeanDefinition的註冊:爲IOC容器提供了更友好的使用方式,是經過一個HashMap來持有載入的BeanDefinition的。在Map中能夠更方便的進行檢索和使用。框架

  依賴注入:函數

  *是否單件模式的Bean,是的話從緩存中取。

  *若是當前沒有此Bean,就從雙親BeanFactory鏈一直向上查找。

  *得到當前Bean的全部依賴Bean,遞歸調用getBean。

  *工廠方法生成

  對象的生成實際上是應用反射機制或CGLIB生出。

  1)生成對象

  *讀取xml文件,獲取beans標籤下的全部bean標籤。

  *利用Class.forName(class).newInstance(),咱們就能夠構造了一個bean的實例了。

  2)依賴對象

  其實依賴對象也是bean實例的一個屬性,那麼其實咱們調用bean實例的setXXX()方法後,就能夠爲他注入依賴對象了,一樣也是利用反射原理。

  *讀取bean下面的全部 <property name="p" ref="persondao">標籤

  *利用自省原理,得到bean的全部屬性的集合,PropertyDescriptor[] ps=Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();

  *判斷設置的 <property name="">標籤中的name是否有在屬性的集合中(ps裏面),好比咱們填寫了標籤 <property name="p"ref="persondao">,那麼咱們應該查找類中是否有Persondao p ;這個屬性,若是沒有的話,就不用設置p的值了。

  *接下去咱們就能夠調用屬性的set方法,爲bean實例設置值了。那麼怎麼獲取set方法呢?咱們能夠調用PropertyDescriptor類的getWriteMethod方法,獲取set方法,而後執行其invoke(要注入到的對象, 要、注入的值),若是沒有set方法的話,咱們天然也就不須要設置依賴對象的值了。

  autowire 

  開發人員都會拿DRY(討厭重複勞動!Don't Repeat Yourself!)和KISS(保持簡單,傻瓜!Keep It Simple, Stupid!)說笑。事實上,這正是優秀開發者必須具有的基本素質,即遵循這兩條效率和敏捷原則。爲簡化DI容器中協做者的管理,Spring引入了Autowiring特性。

  方便之處在減小或者消除屬性或構造器參數的設置,這樣能夠給咱們的配置文件減減肥。

  其實,自動裝配就是讓咱們少些幾個  <ref ="...">.

  eg:當byName時,private Bean3 bean3;  這個bean3必須和<bean id="bean3" class="com.test.model.Bean3"

  parent="abstractBean"> 這個bean3相同.不然不能自動裝配。

  CGLIB

  CGLIB(Code Generation Library)是一個開源項目!是一個強大的,高性能,高質量的Code生成類庫( 比java的反射或動態代理快不少!),它能夠在運行期擴展Java類與實現Java接口。Hibernate用它來實現PO字節碼的動態生成。
   代理爲控制要訪問的目標對象提供了一種途徑。當訪問對象時,它引入了一個間接的層。常常被用來動態地建立代理。JDK的動態代理用起來很是簡單,但它有一個限制,就是使用動態代理的對象必須實現一個或多個接口。若是想代理沒有實現接口的繼承的類,該怎麼辦?如今咱們可使用CGLIB包。
  CGLIB包的底層是經過使用一個小而快的 字節碼處理框架ASM,來轉換 字節碼並生成新的類。除了CGLIB包,腳本語言例如Groovy和BeanShell,也是使用ASM來生成java的字節碼。固然不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。

  

3.AOP原理

  AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程。

  能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態添加功能的一種技術。

  主要的意圖是:將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼

  在Spring中提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。

  面向對象編程主要用於爲同一對象層次的公用行爲建模。它的弱點是將公共行爲應用於多個無關對象模型之間。而這偏偏是面向方面編程適合的地方。有了 AOP,咱們能夠定義交叉的關係,並將這些關係應用於跨模塊的、彼此不一樣的對象模型。AOP 同時還可讓咱們層次化功能性而不是嵌入功能性,從而使得代碼有更好的可讀性和易於維護。它會和麪向對象編程合做得很好。

4.Spring AOP

  1)利用Spring AOP接口實現AOP,主要是爲了指定自定義通知來供spring AOP機制識別。主要接口:前置通知 MethodBeforeAdvice ,後置通知:AfterReturningAdvice,環繞通知:MethodInterceptor,異常通知:ThrowsAdvice 。

  前置方法會在切入點方法以前執行,後置會在切入點方法執行以後執行,環繞會在切入點先執行around的前處理,而後執行切點方法,再執行return處理。最後執行around的後處理。

  spring 處理順序是按照xml配置順序依次處理通知,以隊列的方式存放前通知,以壓棧的方式存放後通知。因此是前通知依次執行,後通知到切入點執行完以後,從棧裏在後進先出的形式把後通知執行。

  Spring AOP 框架對 AOP 代理類的處理原則是:若是目標對象的實現類實現了接口,Spring AOP 將會採用 JDK 動態代理來生成 AOP 代理類;若是目標對象的實現類沒有實現接口,Spring AOP 將會採用 CGLIB 來生成 AOP 代理類。

  JDK動態代理爲何必須使用接口:

  從建立代理函數看起,即public static Object newProxyInstance(ClassLoader loader,   Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException 

  經過源碼能夠看到,這個類第一步生成一個代理類(注意,這裏的參數就是接口列表),Class cl = getProxyClass(loader, interfaces);

  而後經過代理類找到構造參數爲InvocationHandler的構造函數並生成一個新類。

  Constructor cons = cl.getConstructor(constructorParams);//這個有用,在後面細說
  return (Object) cons.newInstance(new Object[] { h });  

  接口起什麼做用呢,因而又看getProxyClass方法的代碼,這個源碼很長,就不細說了。大體分爲三段:

    第一:驗證

    第二:緩存建立新類的結構,若是建立過,則直接返回。(注意:這裏的KEY就是接口列表)

    第三:若是沒有建立過,則建立新類

  建立代碼以下

      long num;
     //得到代理類數字標識 

     synchronized (nextUniqueNumberLock) {
        num = nextUniqueNumber++;
     }

      //得到建立新類的類名$Proxy,包名爲接口包名,但須要注意的是,若是有兩個接口並且不在同一個包下,也會報錯

      String proxyName = proxyPkg + proxyClassNamePrefix + num;
      //調用class處理文件生成類的字節碼,根據接口列表建立一個新類,這個類爲代理類,
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces);
      //經過JNI接口,將Class字節碼文件定義一個新類

      proxyClass = defineClass0(loader, proxyName,
         proxyClassFile, 0, proxyClassFile.length);

   根據前面的代碼Constructor cons = cl.getConstructor(constructorParams);

   能夠猜想到接口建立的新類proxyClassFile 無論採用什麼接口,都是如下結構

    public class $Proxy1 extends Proxy implements 傳入的接口{

    }
  生成新類的看不到源代碼,不過猜想它的執行原理頗有多是若是類是Proxy的子類,則調用InvocationHandler進行方法的Invoke。

  因此,JDK動態代理的原理是根據定義好的規則,用傳入的接口建立一個新類,這就是爲何採用動態代理時爲何只能用接口引用指向代理,而不能用傳入的類引用執行動態類。

 

參考:http://blog.csdn.net/m13666368773/article/details/7802126

http://javacrazyer.iteye.com/blog/794035

http://www.cnblogs.com/frankliiu-java/articles/1896443.html

百度百科

Spring技術內幕

相關文章
相關標籤/搜索