Logo

郎哥编程

Spring的依赖注入

2018-09-23 873

本篇主要介绍Spring的依赖注入。依赖注入是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类,代码如下:

package com.milihua.springprogram.notice;
import com.milihua.springprogram.entity.Teacher;
public class ShortNotice implements NoticeInterface{
private Teacher teacher;
String  message;
/**
     * 构造注入
     * @param accountDao
     */
    public ShortNotice(Teacher inTeacher){
        this.teacher = inTeacher;
    }
   
    public String getTeacherName()
    {
     return this.teacher.getName();
    }
 
public String getMessage() {
    return message;
}
 
public void setMessage(String message) {
    this.message = message;
}
 
@Override
public void sendMessage() {
    // TODO Auto-generated method stub
   
}
}

ShortNotice类依赖实体Teacher类,由ShortNotice类的构造函数注入。

在课程案例SpringProgram项目的config目录下新建structure.xml配置文件,配置文件代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.2.xsd
                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<bean id="teacher"  class="com.milihua.springprogram.entity.Teacher">
    <property name="name"  value="张老师"></property>
</bean>
<bean name="shortNotice" class="com.milihua.springprogram.notice.ShortNotice">
        <!-- 构造方法方式注入Teacher对象-->
        <constructor-arg  ref="teacher"/>
    </bean>
</beans>

构造注入可以传入简单值或对象类型,注入参数使用<constructor-arg>标签。如果注入不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。要解决这个问题,就要确保在构造函数定义的参数顺序,与在配置文件中定义的注入参数顺序一致。

例如,下面的类

package com.milihua.springprogram;
public class Foo {
   public Foo(Bar bar, Baz baz) {
      // ...
   }
}

在配置文件需要做如下定义:

<beans>
<bean id="foo" class=" com.milihua.springprogram.Foo">
   <constructor-arg ref="bar"/>
   <constructor-arg ref="baz"/>
</bean>
 
<bean id="bar" class=" com.milihua.springprogram.Bar"/>
<bean id="baz" class=" com.milihua.springprogram.Baz"/>
</beans>

在配置文件中,也可以使用type 属性显式指定构造函数参数的类型,容器允许使用与简单类型匹配的类型。

例如,下面的类

package com.milihua.springprogram;
public class Foo {
   public Foo(int year, String name) {
      // ...
   }
}

配置文件可以做如下定义:

<beans>
<bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg type="int" value="2001"/>
   <constructor-arg type="java.lang.String" value="Zara"/>
</bean>
</beans>

另外,使用构造函数注入需要注意的是,如果需要向一个对象传递一个引用,需要使用标签的 ref 属性,如果需要直接传递值,那么应该使用如上所示的 value 属性。

2、基于设置函数的依赖注入

将Bean所依赖的对象通过设置函数注入,Bean需要为注入的依赖对象提供设置方法。

在课程案例SpringProgram项目中,发送通知组件类EmailNotice依赖实体类Teacher,向Teacher类发送消息。EmailNotice类提供了设置Teacher类的方法,Spring IOC容器创建EmailNotice和Teacher实例后,调用EmailNotice类设置Teacher类的方法,将Teacher实例注入到EmailNotice实例。EmailNotice类代码如下:

package com.milihua.springprogram.notice;
import com.milihua.springprogram.entity.Teacher;
public class EmailNotice implements NoticeInterface {
 
private Teacher teacher;
String  message;
public String getMessage() {
    return message;
}
public void setMessage(String message) {
    this.message = message;
}
@Override
public void sendMessage() {
    // TODO Auto-generated method stub
    teacher.setClasstime(message + "_邮件发送");
}
public Teacher getTeacher() {
    return teacher;
}
public void setTeacher(Teacher teacher) {
    this.teacher = teacher;
}
 
}

EmailNotice类有值属性和对象引用属性,值属性为message,对象引用属性为teacher。值属性可以通过value注入,对象引用属性需要通过ref注入。注入标签使用<property>。配置文件代码如下。

<beans>
<bean id="teacherZhang" class="com.milihua.springprogram.entity.Teacher">
    <property name="name" value="张老师"></property>
</bean>
<bean id="eamilNotice" class="com.milihua.springprogram.notice.EmailNotice">
    <property name="message" value="8:45上语文课"></property>
    <property name="teacher" ref="teacherZhang"></property>
</bean>
</beans>

Spring IOC容器除了向Bean注入简单对象外,也可以向Bean注入集合对象。如map、list、set、数组等集合对象。

在课程案例SpringProgram项目中新建实体类Student,Student类有info和courses两个属性,info是Map对象,用于记录学生的基本信息,courses是List对象,用于记录学生学习的课程名称。

package com.milihua.springprogram.entity;
import java.util.List;
import java.util.Map;
public class Student {
private List<String> courses;
private Map<String,String> infos;
public List<String> getCourses() {
    return courses;
}
public void setCourses(List<String> courses) {
    this.courses = courses;
}
public Map<String, String> getInfo() {
    return infos;
}
public void setInfo(Map<String, String> info) {
    this.infos = info;
}
}

Spring配置文件的注入代码如下。

<?xml version="1.0" encoding="UTF-8"?>
<bean id="student" scope="prototype" class="com.milihua.springprogram.entity.Account" >
    <!-- 注入map -->
    <property name="infos"> 
        <map> 
          <entry key="姓名" value="张华"></entry> 
          <entry key="年龄" value="11"></entry> 
          <entry key="性别" value="男"></entry> 
        </map> 
  </property> 
  <!-- 注入list -->
   <property name="courses"> 
       <list> 
           <value>语文</value> 
           <value>数学</value> 
           <value>英语</value>
       </list> 
   </property> 
</bean>

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类代码如下:

package com.milihua.springprogram.notice;
import com.milihua.springprogram.entity.Teacher;
public class EmailNotice implements NoticeInterface {
 
private Teacher teacher;
String  message;
public String getMessage() {
    return message;
}
public void setMessage(String message) {
    this.message = message;
}
@Override
public void sendMessage() {
    // TODO Auto-generated method stub
    teacher.setClasstime(message + "_邮件发送");
}
public Teacher getTeacher() {
    return teacher;
}
public void setTeacher(Teacher teacher) {
    this.teacher = teacher;
}
 
}

EmailNotice类依赖于Teacher类,并提供了设置Teacher类的set方法,可以使用Spring的自动装配机制。配置文件代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.2.xsd
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<bean id="teacher"  class="com.milihua.springprogram.entity.Teacher">
    <property name="name"  value="张老师"></property>
</bean>
<bean id="eamilNotice" autowire="byType" class="com.milihua.springprogram.notice.EmailNotice">
    <property name="message" value="8:45上语文课"></property>
</bean>
</beans>

配置文件通过使用<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语句,代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import com.milihua.springprogram.entity.Teacher;
public class QqNotice implements NoticeInterface{
/*
 * teacher为待依赖注入对象
 * 在声明teacher语句前加上@Autowired注解
 *
 *  **/
@Autowired
private Teacher teacher;
String  message;
public String getTeacherName()
    {
     return this.teacher.getName();
    }
 
public String getMessage() {
    return message;
}
 
public void setMessage(String message) {
    this.message = message;
}
 
@Override
public void sendMessage() {
    // TODO Auto-generated method stub
   
}
public Teacher getTeacher() {
    return teacher;
}
}

使用注解前必须在Spring配置文件中注册注解驱动       <context:annotation-config/>,这样注解才能被正确识别。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.2.xsd
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:annotation-config/>
<bean id="teacher"  class="com.milihua.springprogram.entity.Teacher">
    <property name="name"  value="张老师"></property>
</bean>
<bean id="qqnotice" class="com.milihua.springprogram.notice.QqNotice">
    <property name="message" value="8:45上语文课"></property>
</bean>
</beans>

使用@Autowired注解后,只需要在配置文件中定义Bean就可以了,无需再配置Bean之间的关联关系。

测试代码如下:

package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.milihua.springprogram.notice.EmailNotice;
public class AutoIntoTest {
 
public static void main(String[] args) {
    // TODO Auto-generated method stub
ApplicationContext context = new ClassPathXmlApplicationContext("config/autointo.xml");
   
    //从容器中获取通知张老师的组件
    EmailNotice  notice = context.getBean("eamilNotice",EmailNotice.class);
    System.out.println(notice.getTeacher().getName());
}
 
}

@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类的名称。

/*
 * teacher为待依赖注入对象
 * 在声明teacher语句前加上@Autowired注解
 *
 *  **/
@Autowired
@Qualifier("teacher")
private Teacher teacher;

@Resource注解的功能和@Autowired注解功能相近,@Resource有name和type两个主要的属性。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。如果两个属性都未指定,Spring容器将通过反射技术默认按byName模式注入。

例如下面的注解声明:

/*
 * teacher为待依赖注入对象
 * 在声明teacher语句前加上@Autowired注解
 *
 *  **/
@Autowired
    @Qualifier("teacher")
private Teacher teacher;
可以替换为:
/*
   * teacher为待依赖注入对象
   * 在声明teacher语句前加上@Resource注解
   *
   *  **/
  @Resource("teacher")
private Teacher teacher;

课程小结

本文介绍了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即可完成依赖注入的自动装配工作。


代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论