Spring IOC与依赖注入

本文详细介绍了Spring框架中的IoC容器概念及其两种依赖注入方式:设值注入和构造注入。通过具体示例展示了如何配置Bean及其实现类,以及如何通过Spring容器管理Bean之间的依赖关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring IOC的思想:当某个Java实例调用另一个Java实例时,传统的程序设计,通常由调用者来创建被调用者的实例。但是在控制反转/依赖注入模式下,被调用者实例的创建由Spring IOC 容器来创建,然后注入调用者。Spring IOC容器创建实例时,使用了反射机制,即利用Class的forName()方法获取对应的Class对象,这种方式需要传入类的全限定名,因此在xml文件中需要通过class这个元素指定。然后再利用newInstance()获取类的实例,这种方式要求对应类有默认构造器

依赖注入有两种方式:

一、设值注入

        设值注入是指IoC容器使用属性的setter方法来注入被依赖的实例。这种注入方式比较简单、直观。

        下面是Person接口,该接口定义了一个Person规范。

[java]  view plain  copy
  1. public interface Person {  
  2.     //定义使用斧子的方法  
  3.     public void useAxe();  
  4. }  

        Axe接口:

[java]  view plain  copy
  1. public interface Axe {  
  2.     //Axe接口里面有个砍的方法  
  3.     public String chop();  
  4. }  


          Person的实现类。

[java]  view plain  copy
  1. public class Chinese implements Person {  
  2.     private Axe axe;  
  3.     private String name;  
  4.   
  5.     // 设值注入所需的setter方法  
  6.     public void setAxe(Axe axe) {  
  7.         this.axe = axe;  
  8.     }  
  9.   
  10.     public void setName(String name) {  
  11.         this.name = name;  
  12.     }  
  13.   
  14.     // 实现Person接口的userAxe方法  
  15.     public void useAxe() {  
  16.         // 调用axe的chop方法,表明Person对象依赖于Axe对象  
  17.         System.out.println("我是"+name+"用"+axe.chop());  
  18.     }  
  19.   
  20. }  

          上面的代码实现了Person接口的userAxe()方法,实现该方法时调用了axe的的chop()方法,这就是典型的依赖关系。在这里Spring容器的作用就是以松耦合的方式来管理这种调用关系。在上面的Chinese类中,Chinese类并不知道它要调用的axe实例在哪里,也不知道axe实例是如何实现的,它只是需要调用一个axe实例,这个Axe实例将由Spring容器负责注入。

          Axe的实现类:StoneAxe

[java]  view plain  copy
  1. public class StoneAxe implements Axe{  
  2.   
  3.     public String chop() {  
  4.         return "石斧砍柴好慢啊!!!";  
  5.     }  
  6.   
  7. }  

         直到这里,程序依然不知道Chinese类和Axe实例耦合,Spring也不知道!实际上,Spring需要使用XML配置文件来指定实例之间的依赖关系。 Spring采用了XML文件作为配置文件。 对于本应用的XML配置文件如下:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://www.springframework.org/schema/beans"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   
  7.     <!-- 配置Chinese实例,其实现类是Chinese -->  
  8.     <bean id="chinese" class="com.spring.service.impl.Chinese">  
  9.         <!-- 将StoneAxe注入给axe属性 -->  
  10.         <property name="axe" ref="stoneAxe" />  
  11.         <property name="name" value="孙悟空"/>  
  12.     </bean>  
  13.       
  14.       
  15.     <!-- 配置stoneAxe实例 -->  
  16.     <bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" />  
  17. </beans>  

          在配置文件中,Spring配置Bean实例通常会指定两个属性:

          id:指定该Bean的唯一标识,程序会通过id属性值来访问该Bean实例。

          class:指定该Bean的实现类,此处不可再用接口,必须是实现类Spring容器会使用XML解析器读取该属性值,并利用反射来创建该实现类的实例。

 

         从上面可以看出BeanBean之间的依赖关系放在配置文件里组织,而不是写在代码里。通过配置文件的指定,Spring能够精确地为每个Bean注入属性。因此,配置文件里的<bean…/>元素的class属性值不能是接口,而必须是真正的实现类。

 

         Spring会自动接管每个<bean…/>定义里的<property …/>元素定义,Spring会在调用无参数的构造器、创建默认的Bean实例后,调用相应的setter方法为程序注入属性值。<property…/>定义的属性值将不再有该Bean来主动设置、管理,而是接受Spring的注入。

 

         每个Beanid属性是该Bean的唯一标识,程序通过id属性访问BeanBeanBean的依赖关系也是通过id属性关联。

         测试程序:

[java]  view plain  copy
  1. public class BeanTest {  
  2.     public static void main(String[] args) {  
  3.         //创建Spring容器  
  4.         ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");  
  5.         //获取Chinese实例  
  6.         Person person = ctx.getBean("chinese",Person.class);  
  7.         person.useAxe();  
  8.     }  
  9.   
  10. }  

         执行上面的程序,执行结果如下:

         主程序调用PersonuserAxe()方法时,该方法的方法体内需要使用Axe实例,但程序没有任何地方将特定的Person实例和Axe实例耦合在一起,也就是说程序没有为Person实例传入Axe实例,Axe实例由Spring在运行期间注入。

 

         Person实例不仅不需要了解Axe实例的具体实现,甚至无须了解Axe的创建过程。Spring容器根据配置文件的指定,创建Person实例时,不仅创建了Person的默认实例,同时也为该实例依赖注入其所依赖的Axe实例。

 

         BeanBean之间的依赖关系由Spring管理,Spring采用setter方法为目标Bean注入所依赖的Bean,这种方式被称之为设值注入。

 

        从上面的实例我们可以看出,依赖注入以配置文件管理Bean实例之间的耦合,让Bean实例之间的耦合从代码层次分离出来。

 

       Spring IoC容器有如下3个基本要点:

       1、  应用程序的各个组件面向接口编程。面向接口编程可以将各个组件的耦合提升到接口层次,从而有利于项目后期的扩展。

        2、  应用程序的各组件不再由程序主动产生,而是由Spring容器来负责产生,并初始化。

       3、  Spring采用配置文件、或者Annotation来管理Bean的实现类、依赖关系,Spring容器则根据配置文件,利用反射机制来创建时间,并为之注入依赖关系。


二、构造注入

         构造注入就是利用构造器来设置依赖关系的方式。

         Japanese类:

[java]  view plain  copy
  1. public class Japanese implements Person{  
  2.   
  3.     private Axe axe;  
  4.     //默认构造器  
  5.     public Japanese(){  
  6.           
  7.     }  
  8.       
  9.     //构造注入所需的带参数构造器  
  10.     public Japanese(Axe axe){  
  11.         this.axe = axe;  
  12.     }  
  13.       
  14.     public void useAxe() {  
  15.         System.out.println(axe.chop());  
  16.     }  

 

         上面的Chinese类并没有setter方法,仅仅只是提供了一个带Axe属性的构造器,Spring将通过该构造器为Chinese注入所依赖的Bean实例。 构造注入的配置文件需要做一些修改。为了使用构造注入,使用<constructor-arg…/>元素来指定构造器的参数。如下

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://www.springframework.org/schema/beans"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.   
  7.     <!-- 配置Japanese实例 -->  
  8.     <bean id="japanese" class="com.spring.service.impl.Japanese">  
  9.         <!-- 使用构造注入 ,为Japanese实例注入SteelAxe实例-->  
  10.         <constructor-arg ref="stoneAxe"/>  
  11.     </bean>  
  12.       
  13.     <!-- 配置stoneAxe实例 -->  
  14.     <bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" />  
  15. </beans>  


         上面的配置文件使用<contructor-arg…/>元素指定了一个构造器参数,该参数类型是Axe,这指定Spring调用Chinese类里带一个Axe参数的构造器来创建chinese实例,因为使用了有参数的构造器创建实例,所以当Bean实例被创建完成后,该Bean的依赖关系也就已经设置完成

 

        执行效果与设值注入的执行效果一样。但是还是有点却别:创建Person实例中Axe的属性时机不同设值注入式先通过无参数的构造器创建一个Bean实例,然后调用它的setter方法注入依赖关系,而构造注入则是直接调用有参数的构造器,当Bean实例创建完成后,依赖关系也已经完成.


容器中Bean的作用域

支持5种作用域:

1、Singleton:单例模式,在整个SpringIoC容器中,使用singleton定义的Bean将只有一个实例。

2、ProtoType:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。

3、request:对于每次HTTP请求,使用request定义的Bean都将产生一个新的实例,即每次HTTP请求都会产生不同的Bean实例。当然只有在WEB应用中使用Spring时,该作用域才真正有效。

4、session:对于每次HTTPSession,使用session定义的Bean都将产生一个新的实例时,即每次HTTP Session都将产生不同的Bean实例。同HTTP一样,只有在WEB应用才会有效。

5、global session:每个全局的HTTPSession对应一个Bean实例。仅在portlet Context的时候才有效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值