技术交流笔记——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出现的缘由就是为了解耦合。利用一个通用的工厂,来管理各个实现类的建立过程以及在建立过程当中引入的类的依赖关系。这样,各个类在代码层级上,是能够作到“兵不知将将不知兵”的,从而减小对类进行修改、扩展时的工做量。

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

相关文章
相关标签/搜索