技術交流筆記——SPRING IOC 原理

前兩天組織了一次內部的技術交流,主題是spring ioc框架的原理。時間很短,只有一個小時,因此內容比較簡單,主要是幾個題目,和圍繞題目的一些討論。

首先考慮這樣一個場景:
系統中有以下幾個類。
Class A {
  B b;
  C c;
  public void service(){
  Assert b!=null;
  Assert c!= null;
  ....
  }
}

Class B{
  D d;
}

Class C{
  E e;
  F f;
}

Class D{}
Class E{}
Class F{}

在調用A的service時,必須先對它進行初始化,並設置b和c的初始值。初始化B時,必須先初始化d;初始化C時,必須先初始化e和f。

問題1:使用new的方法,寫出完整的對A進行初始化、並調用service()服務的步驟。
參考答案:
class A{
public A(){
this.b=new B();
this.c=new C();
}
……
}
Class B{
public B(){
this.d=new D();
}
}
Class C{
public C(){
this.e=new E();
this.f=new F();
}
}
調用代碼:
public static void main(String[] args){
A a=new A();
a.service();
}

問題2: 因爲需求變動,類A被修改成:

Class A {java

//B b;spring

D d;數據庫

C c;框架

public void service(){ide

Assert d!=null;this

Assert c!= null;spa

....orm

}對象

}接口

請修改問題1中的代碼,使A可以正常初始化。

參考答案:

class A{
public A(){
this.d=new D();
this.c=new C();
}
……
}
……
調用代碼:
public static void main(String[] args){
A a=new A();
a.service();
}

問題三、 因爲需求變動,類A的服務service()被拆分紅兩部分service()和service_2(), 而且,系統中先有的調用service()服務的代碼,有一些要改用service_2()。類A的 定義已修改以下:

Class A {

D d;

C c;

G g;

public void service(){

Assert d!=null;

Assert c!= null;

....

}

public void service_2(){

Assert g!= null;

……

}

}

請修改問題2中的代碼,以適應新的需求。

參考答案:

class A{
public A(D _d, C _c){
this.d=_d;
this.c=_c;
}

public A(G _g){
this.g=_g;
}
……
}
……
調用代碼:
public static void main(String[] args){
A a=new A(new D(),new C());
a.service();

A a_2=new A(new G());
a.service_2();
}
問題四、 公司發現類 A 可使用單例實現。請修改上述代碼以實現此需求。
參考答案:
class A{
private static A a=new A(new D(),new C());
private static A a_2=new A(new G());

public static A getA(){
return a;
}

public static A getA_2(){
return a_2;
}

private A(D _d, C _c){
this.d=_d;
this.c=_c;
}

private A(G _g){
this.g=_g;
}
……
}
……
調用代碼:
public static void main(String[] args){
A a=A.getA();
a.service();

A a_2=A.getA_2();
a.service_2();
}


直接使用new關鍵字,是最基礎的實例化對象的方式。使用這種方式直接進行實例化,最直接的問題是將服務的調用者和服務的實現類耦合在了一塊兒。如問題1中,main方法中直接new一個A的實例,那麼它就和類A耦合起來了。A和B、C,B和D,C和E、F,也是同樣。

直接耦合帶來的麻煩,在問題三、4中體現得最爲明顯。因爲需求變動而致使的對類A進行的修改,都引起了對調用代碼的修改。參考答案中只給出了一處調用。可是設想一下,若是A提供的是基礎服務,好比數據庫操做,或者一些基礎的業務邏輯。那麼,系統中會有多少對A的引用?一旦數據庫從Oracle遷移到MySQL,或者某服務針對江西提供一種邏輯、針對湖南提供另外一種邏輯(這種狀況在我參與的項目中真實出現過),那麼當修改類A時,系統中會有多少處代碼要作變動?

但願這部份內容能幫助理解這樣直接耦合會帶來什麼樣的問題。
而解決問題的辦法,無疑就是解耦合。問題2和問題4給了咱們一種思路。
問題2將類A的建立所有封裝在了無參數構造方法A()裏面。這樣,建立A的過程就和調用服務的過程徹底隔離開了:調用A的時候徹底沒必要知道,要建立A須要作些什麼工做。惋惜這種方式有點脆弱,遇到問題3就不行了,由於A()方法只能定義一個。它沒法實現兩種建立邏輯。
問題4延續了問題2的思路,把建立A的邏輯封裝起來。調用代碼中只須要獲取實例並調用服務,而不須要考慮A是如何建立的。

這其實就是工廠模式的思路。

問題五、請使用工廠模式,改寫問題4中的代碼。
參考答案:

Public abstract class Factory {

Public static Factory aFactory(){

Return new FactoryA ();

}


Public static Factory a2Factory(){

Return new FactoryA_2();

}

Create();

}

Public class FactoryA implements Factory {

@Override

Public A Create(){

return new A(new D(),new C());

}

}


Public class FactoryA_2 implements Factory {

@Override

Public A Create(){

return new A_2(new G());

}

}

……

調用代碼:

public static void main(String[] args){

A a = Factory.aFactory().Create();

a.service();


A a_2 = Factory.a2Factory().Create();

a_2.service_2();

}


問題六、將service()和service_2()方法提取到接口中,再修改問題5中的代碼。

參考答案:略


問題七、用問題6的思路,爲其它類(B/C/D/E/F/G)等建立工廠

參考答案:略

問題7的出現帶來了一個新的困擾:那麼多的類,要寫那麼多的工廠……尤爲是,當咱們對現有系統進行改造或者重構時,這種重複工做量會有多少,可想而知。


問題8:爲了簡化問題7的工做,公司定義了一個通用的工廠類

public Class Factory{

/**

* 根據入參,調用指定類的默認構造方法(無參數構造方法),生成一個指定的類的實例。若是找不到指定的類,或者指定的類沒有可用的構造方法,則返回null

* <p />

* 例如,調用

* String a = Factory.product(java.lang.String),將返回一個」」。等同於調用了 String a = new String();

*

* @param className 用戶指定的類名。必須是全路徑名,

* @return 用戶指定的類的一個實例,或者是null

*/

public static Object product(String className){

}

}

請將①處的代碼補充完整。

這裏就須要用到java的反射機制了。



綜合上述問題,思考一下:爲何會出現Spring IOC框架?它的基本原理是什麼?

最簡單的說,IOC出現的緣由就是爲了解耦合。利用一個通用的工廠,來管理各個實現類的建立過程以及在建立過程當中引入的類的依賴關係。這樣,各個類在代碼層級上,是能夠作到「兵不知將將不知兵」的,從而減小對類進行修改、擴展時的工做量。

它的基本原理,很明瞭,就是反射+工廠。

相關文章
相關標籤/搜索