黄晓童SPRING学习笔记:对DI的深入探究

Spring的定义
对DI的初步理解
对AOP的初步理解
对DI的深入探究
对AOP的深入探究
Spring的事务管理
Spring MVC

 spring的特性之一是DI,而DI的关键在于bean的装配,即创建系统各组件之间的协作关系。而组件是存活在spring容器中的,容器是spring的核心,spring提供多种容器的实现。第一种是bean工厂,即BeanFactory接口;第二种是ApplicationContext接口。BeanFactory是最简单的容器,只提供了基础的依赖注入支持;而后者是在BeanFactory基础之上的,提供了更多的系统服务。
 Spring中有多种Beanfactory的实现,其中最常见的一种就是根据XML配置文件来装载bean的XmlBeanFactory。但是要创建一个XmlBeanFactory实例需要传递一个Resource类型的实例给构造函数,正是此Resource对象提供了XML文件给BeanFactory。因此,创建一个Beanfactory可以这样做:

BeanFactory  factory  =  new  XmlBeanFactory( new FileSystemResource( “C:/abc.xml” ) );

 
这是通过文件系统来读取xml文件初始化BeanFactory的,还有许多种方法,比如ClassPathResource、InputStreamResource等。但是,执行上述代码并没有实例化任何一个bean,要实例化一个bean还要调用getBean方法:factory.getBean(“beanName”); 当调用过该方法之后,BeanFactory会依据xml配置文件设置该bean的属性并实例化它。
 然而,很多时候我们需要一些更强大的功能,这时我们就需要用到ApplicationContext了。而且在大多数情况下,我们是会使用后者的。然而spring的应用上下文对象也很好用,载入一个上下文很简单:

ApplicationContext context = new FileSystemXmlApplicationContext(“C:/abc.xml”);

 
或者像这样:

ApplicationContext context = new ClassPathXmlApplicationContext(“abc.xml”);

 
 这时要获得一个bean和BeanFactory时的一样,毕竟ApplicationContext是继承自BeanFactory的。
 下面将通过一些例子来由浅入深的进行阐述。一年一度的Java天才大赛来了,每个参赛者都可以表演任何节目。因此定义一个接口,每位参赛者都要实现该接口:

public  interface  Performer
{ public  void  perform(); }

 

首先出场的是一位魔术师,它是一个简单的bean:

public  class  Juggler  implements  Performer {
  private  int  beanBags = 3;
   
  public  Juggler(int  beanBags) {
    this.beanBags = beanBags;  
  }

  public  void  perform(){
    System.out.println("JUGGLING " + beanBags + " BEANBAGS");
  }
}

 

传统的注入该bean方式如下:

<bean  id=”duke”  class=”com.alibaba.Juggler”  / >

 

spring容器装载该bean时,会调用该类的默认构造函数,因此该类的实例的beanBags变量的值是3。我们还可以通过如下的配置方法让该类的实例的变量值是15:

<bean  id=”duke”  class=”com.alibaba.Juggler” >
 <constructor-arg  value=”15”  />
</bean>

 

这样,spring注入该bean的时候,就会调用该类的重载构造函数。如果对于一些特殊的构造函数的参数中包含对象的(非基本类型),可以通过如下方式指定参数的值:

<constructor-arg  ref=”object-Bean-Id”  />

 

其中“object-Bean-Id”就是另一个bean的id

下面,为了演示spring的setter注入方式,我们荣幸的请到了出色的乐器演奏家Kenny。

public  class  InstrumentKenny  implements  Performer 
{
  public void perform() 
 {
    System.out.print("Playing " + song + " : ");
    instrument.play();
  }
  
  private String song;
  public void setSong(String song) {
    this.song = song;
  }
  
  private Instrument instrument;
  public void setInstrument(Instrument instrument) {
    this.instrument = instrument;
  }
}

 

当然,我们需要定义一个乐器接口Instrument:

public  interface  Instrument
{
  public void play();
}

 

现在,我们创造一个萨克斯风乐器:

public  class  Saxophone  implements  Instrument
{
 public void play() 
 {
 System.out.println("TOOT TOOT TOOT");
 }
}

 

这时,我们只需要进行如下配置,就可以让Kenny去演奏萨克斯风了:

<bean  id="saxophone"  class="com.alibaba.Saxophone" />
<bean  id=”kenny”  class=”com.alibaba.InstrumentKenny”>
 <property  name=”song”  value=”Jingle bells” />
 <property  name=”instrument”  ref=” saxophone” />
</bean>

 

 其实我们会发现,使用setter注入和使用构造函数注入,没什么区别,代码几乎相同。如果,kenny很聪明并且还会演奏钢琴的话,我们只需要声明一个钢琴类,再修改一下配置文件,ref=” piano” 即可。几乎不需要修改任何已有代码,这样就实现了松耦合的目的。
 现在,kenny可以演奏任何实现Instrument接口的乐器了,然而,这些乐器也可以被其他人所演奏,因此,就产生了乐器的个人卫生问题(萨卡斯风是要使用嘴吹的)。我们可以通过注入内部bean来解决这个问题:

<bean  id=”kenny”  class=”com.alibaba.InstrumentKenny”>
 <property  name=”song”  value=”Jingle bells” />
 <property  name=”instrument”>
  <bean  class=”com.alibaba. saxophone” />
 </property>
</bean>

 

 内部bean不能被其他bean所复用,因此该bean也就没有必要有id属性。当然,在构造函数注入方式下也可以使用内部bean。

 下面,将阐述一下spring的自动注入。spring提供了四种自动装配类型:byname、bytype、constructor、autodetect。其中,byname使用最为广泛。现在让我们来回忆一下kenny演奏萨克斯风的配置:

<bean  id="saxophone"  class="com.alibaba.Saxophone" />
<bean  id=”kenny”  class=”com.alibaba.InstrumentKenny”>
 <property  name=”song”  value=”Jingle bells” />
 <property  name=”instrument”  ref=” saxophone” />
</bean>

 
我们可以将上面的配置改成如下这样,以实现spring的自动注入:

<bean  id=" instrument"  class="com.alibaba.Saxophone" />
<bean  id=”kenny”  class=”com.alibaba.InstrumentKenny”  autowire=”buName”>
 <property  name=”song”  value=”Jingle bells” />
</bean>

 

 然而,我们也可以使用混合的方式,如:在设置autowire=”byType”时,显式的装配某个bean,而不需要spring自己去寻找一个type相同的bean,以避免有多个相同类型的bean时spring抛出异常。
 由此我们可以发现,spring的自动装配存在很多问题,其中最大的缺点就是缺乏代码的可读性和透明性。
 我们已经了解了spring对bean的装载的基础知识。下面我们来说一下spring中bean的范围。在spring中,bean的范围可以是如下几种:singleton、prototype、request、session、global-session,比如:

<bean  id=””  class=””  scope=””  />

 

其中,如果不指定scope的话,默认会是单例的,即singleton的,意思是在整个spring容器中,该bean只有一个实例;而prototype则与之相反,每次使用的时候都会创建一次。当然,我们也可以通过其他手段来实现单例:比如我们可以设置bean标签的factory-method属性来指定该类的工厂方法。
 在bean生命周期中有两个重要的环节:初始化和销毁。为一个bean指定一个初始化方法和销毁方法很简单,只要设置bean标签的init-method属性和destroy-method即可。当然我们还有另一个方法:实现InitializingBean和DisposableBean接口,然后,在该类中实现afterPropertiesSet方法和destroy方法,以实现bean的初始化和销毁。
 下面我们来看一下bean的继承。spring的bean标签中有两个属性:parent和abstract。前者相当于java里的extends,后者则是告诉spring容器,该bean不能被实例化。举个例子,前面我们说过,kenny是一个擅长吹萨克斯的选手,他的配置文件如下:

<bean  id="saxophone"  class="com.alibaba.Saxophone" />
<bean  id=”kenny”  class=”com.alibaba.InstrumentKenny”>
 <property  name=”song”  value=”Jingle bells” />
 <property  name=”instrument”  ref=” saxophone” />
</bean>

 
 可是,这个比赛不止kenny一个人会吹萨克斯,现在又有一个david也吹萨克斯,而且也演奏“Jingle bells”。这样的话,如果配置两条除id以外都相同的bean多少有些累赘。这时我们就需要使用到bean的继承特性。配置如下:

<bean  id=”baseSaxophonist”  class=”com.alibaba.Instrument”  abstract=”true” >
 <property  name=”song”  value=”Jingle bells” />
 <property  name=”instrument”  ref=” saxophone” />
</bean>
<bean  id=”kenny” parent=”baseSaxophonist” />

 
 当然,如果现在又来了一位选手,他会吹萨克斯,但是演奏的不是“Jingle bells”,这是否意味着已有的父bean不能使用了呢?当然不是的,我们还可以覆盖父bean的属性。比如:

<bean  id=”frank” parent=”baseSaxophonist” >
 <property  name=”song”  value=”another song” />
</bean>

 

 然而,声明父bean时我们也可以不指定bean的class属性,具体的class由子bean去指定,这样,我们就可以将一些常用到的属性提出到父bean,让各个bean去继承,而各子bean不必是相同的类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值