依赖注入是Spring协调不同Bean实例之间的合作而提供的一种工作机制,在确保Bean实例之间合作的同时,并能保持每个Bean的相对独立性。通过本篇的学习,可以达成如下目标。
● 理解基于构造函数的依赖注入
● 理解基于设置函数的依赖注入
● 基于自动装配的依赖注入
● 基于注解的依赖注入
在Spring框架下,当Bean实例 A运行过程中需要引用另外一个Bean实例B时,Spring框架会创建Bean的实例B,并将实例B通过实例A的构造函数、set方法、自动装配和注解方式注入到实例A,这种注入实例Bean到另外一个实例Bean的过程称为依赖注入。
在课程案例SpringProgram项目中,事务组件类EmailNotice内部引用了实体类Teacher类,用于向Teacher类发送消息,因此EmailNotice类依赖于Teacher类。Spring IOC容器会在创建EmailNotice类和Teacher类的实例后,将Teacher类的实例注入到EmailNotice类的实例中。
依赖注入的好处就是尽可能隔离Bean之间的代码耦合,提高Bean重用的可能性,并尽量降低程序代码的维护难度。Spring框架通过依赖注入技术将不同的Bean融合起来,完成复杂业务操作,但又确保了每个Bean相对的独立性。
Spring框架提供了构造函数注入、设置方法注入、自动装配注入和注解注入四种注入方式,下面分别进行讨论并说明。
1、 基于构造函数的依赖注入
构造函数注入就是通过Bean类的构造方法,将Bean所依赖的对象注入。构造函数的参数一般情况下就是依赖项,spring容器会根据bean中指定的构造函数参数来决定调用那个构造函数。
在课程案例SpringProgram项目中新建发送通知组件类ShortNotice类,代码如下:
ShortNotice类依赖实体Teacher类,由ShortNotice类的构造函数注入。
在课程案例SpringProgram项目的config目录下新建structure.xml配置文件,配置文件代码如下:
构造注入可以传入简单值或对象类型,注入参数使用<constructor-arg>标签。如果注入不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。要解决这个问题,就要确保在构造函数定义的参数顺序,与在配置文件中定义的注入参数顺序一致。
例如,下面的类
在配置文件需要做如下定义:
在配置文件中,也可以使用type 属性显式指定构造函数参数的类型,容器允许使用与简单类型匹配的类型。
例如,下面的类
配置文件可以做如下定义:
另外,使用构造函数注入需要注意的是,如果需要向一个对象传递一个引用,需要使用标签的 ref 属性,如果需要直接传递值,那么应该使用如上所示的 value 属性。
2、基于设置函数的依赖注入
将Bean所依赖的对象通过设置函数注入,Bean需要为注入的依赖对象提供设置方法。
在课程案例SpringProgram项目中,发送通知组件类EmailNotice依赖实体类Teacher,向Teacher类发送消息。EmailNotice类提供了设置Teacher类的方法,Spring IOC容器创建EmailNotice和Teacher实例后,调用EmailNotice类设置Teacher类的方法,将Teacher实例注入到EmailNotice实例。EmailNotice类代码如下:
EmailNotice类有值属性和对象引用属性,值属性为message,对象引用属性为teacher。值属性可以通过value注入,对象引用属性需要通过ref注入。注入标签使用<property>。配置文件代码如下。
Spring IOC容器除了向Bean注入简单对象外,也可以向Bean注入集合对象。如map、list、set、数组等集合对象。
在课程案例SpringProgram项目中新建实体类Student,Student类有info和courses两个属性,info是Map对象,用于记录学生的基本信息,courses是List对象,用于记录学生学习的课程名称。
Spring配置文件的注入代码如下。
3、基于自动装配的依赖注入
前面讨论了基于构造函数的依赖注入和基于设置函数的依赖注入。了解了在Spring配置文件中使用<constructor-arg>和<property>标签注入Bean的依赖对象。Spring 容器还可以在不使用<constructor-arg>和<property> 标签的情况下自动装配相互协作的 bean 之间的关系,这有助于减少在Spring配置文件中编写大量的注入语句。
Spring的自动装配有三种模式:byType(类型模式),byName(名称模式)、constructor(构造函数模式)。
在byType模式中,Spring IOC容器会基于反射查看Bean定义的类。当Spring 容器发现Bean被设置为自动装配的byType模式后,它会根据参数类型在Spring容器中查找与参数类型相同的被依赖Bean对象,如果已经创建,则会把被依赖的对象自动注入到Bean中,如果没有创建,则不会注入。注入过程需要借助Bean提供的设置方法来完成,否则注入失败。
在课程案例SpringProgram项目中,EmailNotice类代码如下:
EmailNotice类依赖于Teacher类,并提供了设置Teacher类的set方法,可以使用Spring的自动装配机制。配置文件代码如下:
配置文件通过使用<bean>的autowire属性启动名称为eamilNotice的自动装配功能。
在byName模式中,Spring IOC容器会根据定义Bean类的属性名称,在Spring容器中查找与Bean类属性名称相同的其它Bean名称进行匹配,如果找到则注入依赖bean。
类似于byName和byType模式,constructor(构造函数模式)适用于构造函数参数类型,Spring IOC容器会根据定义Bean类的构造函数给出的参数类型,在Spring容器中查找与其类型相匹配的其它Bean类,如果找到则注入依赖Bean。
自动装配最大的问题在于匹配失败后,Spring容器将不会向Bean注入任何依赖对象,就会导致Bean获取不到所依赖的对象,当Bean使用该依赖对象时,就会发生错误。因此,在可能的情况下尽可能使用手动装配。
4、基于注解的依赖注入
前面依赖注入都需要在配置文件中手动配置,当需要配置较多Bean类时,需要做大量的手动部署工作,这显然不妥。在Spring2.5之后,Spring增加了注解方式注入,可以解决较多Bean类依赖注入的问题。
Spring主要提供了@Autowired和@Resource注解模式,下面也重点讨论这两种注解模式。
@Autowired 注解,可以对Bean类成员变量、方法及构造函数进行标注,完成依赖注入的自动装配工作。使用@Autowired可以省略Bean类的待依赖注入对象的set方法,@Autowired默认情况下按照依赖注入对象的类型自动进行匹配。加入@Autowired注解的方式是在Bean类依赖注入对象的前面加上@Autowired语句。
例如,在课程案例SpringProgram项目中新建发送通知类QqNotice,QqNotice类依赖Teacher类,在声明Teacher类变量的前面加上@Autowired语句,代码如下:
使用注解前必须在Spring配置文件中注册注解驱动 <context:annotation-config/>,这样注解才能被正确识别。
使用@Autowired注解后,只需要在配置文件中定义Bean就可以了,无需再配置Bean之间的关联关系。
测试代码如下:
@Autowired还提供required的属性,用来处理当注入的Bean实例不存在的情况。required为true时,如果注入的Bean实例不存在,程序会抛出异常;required为false时,如果注入的Bean实例不存在,程序会忽略。由于默认情况下@Autowired是按类型匹配的(byType),如果需要按名称(byName)匹配,可以使用@Qualifier注解与@Autowired结合。
例如,修改QqNotice类,加入@Qualifier注解@Qualifier("teacher"),Spring容器将会在容器中注册的所有Bean实例中查找名称为“teacher”的Bean实例并注入,“teacher”为在Spring配置文件中定义Bean类的名称。
@Resource注解的功能和@Autowired注解功能相近,@Resource有name和type两个主要的属性。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。如果两个属性都未指定,Spring容器将通过反射技术默认按byName模式注入。
例如下面的注解声明:
课程小结
本文介绍了Spring提供的四种依赖注入方式,分别是基于构造函数的依赖注入、基于设置函数的依赖注入、基于自动装配的依赖注入、基于注解的依赖注入。
基于构造函数的依赖注入。Bean类依赖外部类时,可以在Bean类内部声明该依赖类,并在提供的构造函数参数中声明该类。Spring创建Bean实例时,Spring容器会根据Bean中指定的构造函数参数注入外部类。
基于设置函数的依赖注入。Bean类依赖外部类时,可以在Bean类内部声明该依赖类,并提供该设置该依赖类的set方法。Spring容器会根据Bean类提供的set方法,将外部依赖类注入到Bean中。
基于自动装配的依赖注入。该模式自动装配相互协作的 bean 之间的关系,Spring的自动装配有三种模式:byType(类型模式),byName(名称模式)、constructor(构造函数模式)。Spring的自动装配有助于减少在Spring配置文件中编写大量的注入语句。
基于注解的依赖注入。在Spring2.5之后,Spring增加了注解注入。当Bean类依赖外部类时,只要对Bean类所依赖的类成员变量、方法及构造函数进行标注,Spring即可完成依赖注入的自动装配工作。