详解Spring中bean的几种注入方式

首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入。依赖注入是指:让调用类对某一接口的实现类的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖。

Spring容器中支持的依赖注入方式主要有属性注入、构造函数注入、工厂方法注入。接下来将为大家详细介绍这三种依赖注入的方式以及它们的具体配置方法。

1.属性注入

属性注入即通过setXXX( )方法注入bean的属性值或依赖对象。由于属性注入方式具有可选择性和灵活性高的特点,因此它也是实际开发中最常用的注入方式。

Spring首先会调用bean的默认构造函数实例化bean对象,然后再通过反射的方法调用set方法来注入属性值。

属性注入要求bean提供一个 默认的构造函数 ,并且得为需要注入的属性提供 set方法 。

TIps:所谓默认的构造函数,即不带参数的构造函数。如果类中没有自定义任何构造函数,则系统(JVM)会自动生成一个不带参的默认构造函数,如果类中显式的自定义了有参数的构造函数,则系统就不会在自动生成默认构造函数,需要自己手动再加一个无参的构造函数。

下面通过一个实例来演示Spring中bean的属性注入方式:

编写一个user类:

package com.Kevin.bean;
/**
 * 创建一个类测试bean的属性注入方式
 * @author Kevin
 *
 */
public class User {
  private String username;
  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }
}

配置文件:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="user" class="com.Kevin.bean.User">
    <property name="username">
      <value>Kevin</value>
    </property>
  </bean>
</beans>

其中,每个属性值对应一个property标签,name属性值为类中属性的名称。在bean实现类中拥有与其对应的实现方法setUsername( )。

Tips:Spring只会检查bean中是否含有setter方法,而对是否有对应的属性变量则不作具体要求,但按照约定俗成的规则我们最好为其设定相应的属性变量。

Spring中<property>标签的命名规范:

  1. Spring的<property>标签所指定的属性名称和bean实现类的setter方法满足Sun JavaBean的属性命名规范,即XXX的属性对应setXXX( )的方法。

一般情况下,java的属性变量名都以小写字母开头,但考虑到一些特殊意义的英文缩略词,java bean也允许一些大写字母开头的变量名。但必须满足以下两点:

  1. 变量的前两个字母要么全部大写,要么全部小写;
  2. 但以一般编程习惯来说,属性名最好全部使用小写字母,方便编程和阅读。

对于属性注入方式来说,只能人为的在配置文件中提供保证,而无法在语法级别提供保证。此时就需要使用构造函数注入这种方式,以此来更好的满足要求。

2.构造函数注入

构造函数注入是除属性注入之外的另一种常用的注入方式,它可以保证一些必要的属性在bean实例化时就得到了设置,并在实例化后就可以使用。

使用构造函数注入的前提是: bean必须提供带参的构造函数。

对于构造函数的注入,配置文件可以有以下几种方式:

  1. 按类型匹配入参
  2. 按索引匹配入参
  3. 联合使用类型和索引匹配入参
  4. 通过自身类型反射匹配入参

【按类型匹配入参方式】

编写bean代码:

package com.Kevin.bean;
/**
 * 编写bean测试按类型匹配入参方式
 * @author Kevin
 *
 */
public class Person {
  private String name;
  private Integer age;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Integer getAge() {
    return age;
  }
  public void setAge(Integer age) {
    this.age = age;
  }
}

编写配置文件:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="person" class="com.Kevin.bean.Person">
    <constructor-arg type="String">
      <value>Kevin</value>
    </constructor-arg>
    <constructor-arg type="Integer">
      <value>20</value>
    </constructor-arg>
  </bean>
</beans>

Spring的配置文件采用和元素标签顺序无关的配置策略,因此可以在一定程度上保证配置信息的确定性。

那么当bean中的构造函数的 多个类型参数一样时 ,按照类型匹配入参的这种方式容易产生混淆,此时就需要使用另一种方式:按照索引匹配入参。

【按照索引匹配入参】

编写bean代码:

package com.Kevin.bean;
/**
 * 编写bean测试按照索引方式入参
 * @author Kevin
 *
 */
public class Student {
  private String name;
  private String gender;
  private Double score;
  public Student(String name, String gender, Double score) {
    super();
    this.name = name;
    this.gender = gender;
    this.score = score;
  }
}

配置文件编写如下:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="student" class="com.Kevin.bean.Student">
    <constructor-arg index="0" value="Kevin"></constructor-arg>
    <constructor-arg index="1" value="Male"></constructor-arg>
    <constructor-arg index="2" value="66"></constructor-arg>
  </bean>
</beans>

Tips: 在属性注入时,Spring按java bean的规范确定配置 属性 和对应的 setter方法 ,并使用java反射机制调用属性的setter方法完成属性注入。但java反射机制并 不会记住构造函数的入参名 ,因此我们不能通过制定构造函数的入参名称来进行构造函数的配置,所以我们只能通过 入参的类型及索引 来间接完成构造函数的属性注入。

【联合使用类型和索引匹配入参】

在某些复杂的配置文件当中,需要使用type和index同时出马才能完成构造函数的参数注入。下面使用一个实例来演示。

编写bean:

package com.Kevin.bean;
/**
 * 编写bean测试联合使用类型和索引匹配入参
 * @author Kevin
 *
 */
public class Teacher {
  private String name;
  private String address;
  private double salary;
  private int age;
  public Teacher(String name, String address, double salary) {
    super();
    this.name = name;
    this.address = address;
    this.salary = salary;
  }
  public Teacher(String name, String address, int age) {
    super();
    this.name = name;
    this.address = address;
    this.age = age;
  }
}

在这个类中,有两个重载的构造函数,他们都有三个参数,在这种情况下使用type和index的方法都不能完成要求,这时候就需要他们两个属性同时使用了。

配置文件:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="teacher" class="com.Kevin.bean.Teacher">
    <constructor-arg index="0" type="String">
      <value>Kevin</value>
    </constructor-arg>
    <constructor-arg index="1" type="String">
      <value>China</value>
    </constructor-arg>
    <constructor-arg index="2" type="int">
      <value>20</value>
    </constructor-arg>
  </bean>
</beans>

可以看到其实重点在于第三个入参的类型,所以我们在配置文件中指定了索引和类型,这样便可以使得Spring知道对哪个构造函数进行参数注入了。

Tips: 加入我们得配置文件中存在歧义问题,Spring容器是可以正常启动的,并不会报错,它将随机采用一个匹配的构造函数实例化bean。而随机选择的构造函数可能并不是用户所需要的,所以我们在编程时要小心避免出现这种歧义情况。

【通过自身类型反射匹配入参】

如果bean构造函数入参的类型是可辨别的,由于java反射机制可以获取构造函数入参的类型,即使构造函数的注入不提供类型和索引的信息,Spring依旧可以完成构造函数信息的注入。例如下面实例中Manager类的构造函数的入参类型就是可以辨别的。

编写Manager类:

package com.Kevin.bean;
/**
 * 编写Bean测试通过自身类型反射匹配入参方式
 * @author Kevin
 *
 */
public class Manager {
  private String name;
  private Double salary;
  private Person person;
  public Manager(String name, Double salary, Person person) {
    super();
    this.name = name;
    this.salary = salary;
    this.person = person;
  }
}

编写配置文件:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="manager" class="com.Kevin.bean.Manager">
    <constructor-arg>
      <value>Kevin</value>
    </constructor-arg>
    <constructor-arg>
      <ref bean="user"/>
    </constructor-arg>
    <constructor-arg>
      <ref bean="person"/>
    </constructor-arg>
  </bean>
</beans>

以上几种方法都可以实现 构造函数参数的注入 ,但是为了避免问题的发生,还是建议使用 显式的index和type 来配置构造函数的入参信息。

3.工厂方法注入

工厂方法是应用中被经常使用的设计模式,也是 控制反转 和 单实例设计思想 的主要实现方法。工厂类负责创建一个或多个工厂类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类实例。

工厂类对外屏蔽了目标类的实例化步骤,调用者甚至根本不用指定具体的目标类是什么。由于Spring容器以框架的方法提供工厂方法的功能,并以透明的方式开放给开发者。因此很少需要手工编写工程方法。但在一些遗留系统或第三方类库中还是会碰到工程方法,此时便可以使用Spring工厂注入的方法来进行Spring的注入。

Spring工厂注入的方法可以分为 静态 和 非静态 两种。

【非静态工厂方法】

有些工厂方法是非静态的,必须实例化工厂类之后才能调用工厂方法。下面通过一个实例来演示。

编写工厂类:

package com.Kevin.factorybean;
/**
 * 编写工厂类测试非静态工厂方法注入
 * @author Kevin
 *
 */

public class BookFactory {
  public Book buyBook(){
    Book book = new Book();
    book.setName("Think in Java");
    return book;
  }
}

配置文件编写:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="bookFactory" class="com.Kevin.factorybean.BookFactory"></bean>
  <bean id="book" factory-bean="bookFactory" factory-method="buyBook"></bean>
  </beans>

由于bookFactory的工厂方法不是静态的,因此需要先定义一个工厂类的bean,然后通过 factory-bean 属性来引用工厂bean实例。再通过属性 factory-method 来指定对应的工厂方法。

【静态工厂方法】

很多工厂类方法都是静态的,这意味着无需创建工厂类实例的情况下就可以调用工厂类方法。因此静态工程方法比非静态工厂方法的调用更加方便简洁。下面通过一个实例来演示静态工厂方法。

编写factory类:

package com.Kevin.factorybean;
/**
 * 编写工厂类测试静态工厂方法注入
 * @author Kevin
 *
 */
public class CarFactory {
  public static Car createCar(){
    Car car = new Car();
    car.setBrand("Lamborghini");
    return car;
  }
}

编写配置文件:

<?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" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

  <!-- 配置对象 -->
  <bean id="car" class="com.Kevin.factorybean.Car" factory-method="createCar"></bean>
</beans>

总结

Spring提供了三种可供选择的注入方式,但在实际应用中,我们究竟该选择哪种注入方式,并没有统一的标准,如下是一些可以参考的理由:

构造函数注入理由:

  1. 构造函数保证重要属性预先设置;
  2. 无需提供每个属性的setter方法,减少类的方法个数;
  3. 可以更好地封装类变量,避免外部错误调用。

属性注入理由:

  1. 属性过多时,构造函数变的臃肿;
  2. 构造函数注入灵活性不强,有时需要为属性注入null值;
  3. 多个构造函数时,配置上产生歧义,复杂度升高;
  4. 构造函数不利于类的继承和扩展;
  5. 构造函数注入会引起循环依赖的问题。

其实Spring为我们注入参数提供了这么多方法,那么这些方法必然有他们存在的道理,每个方法在某一问题上会有独特的优势,我们只需要按照我们具体的使用需求选择适合的方法来使用就好了,但一般不太推荐工厂方法注入。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring实战之Bean的作用域singleton和prototype用法分析

    本文实例讲述了Spring实战之Bean的作用域singleton和prototype用法.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/

  • Spring @Bean vs @Service注解区别

    今天跟同事讨论了一下在Spring Boot中,是使用@Configuration和@Bean的组合来创建Bean还是直接使用 @Service等注解放在类上的方式.笔者倾向于使用第一种,即@Configuration和@Bean的组合. 先来看一个例子,目标是创建SearchService的一个Bean. 直接使用@Service的方式: // SearchService.java package li.koly.search; import java.util.List; public in

  • Spring中多配置文件及引用其他bean的方式

    Spring多配置文件有什么好处? 按照目的.功能去拆分配置文件,可以提高配置文件的可读性与维护性,如将配置事务管理.数据源等少改动的配置与配置bean单独分开. Spring读取配置文件的几种方式: 1.使用Spring自身提供的ApplicationContext方式读取 在Java程序中可以使用ApplicationContext两个实现类ClassPathXmlApplicationContext以及FileSystemXmlApplicationContext来读取多个配置文件,他们的

  • Spring的自动装配Bean的三种方式

    spring的自动装配功能的定义:无须在Spring配置文件中描述javaBean之间的依赖关系(如配置<property>.<constructor-arg>).IOC容器会自动建立javabean之间的关联关系. 如果没有采用自动装配的话,手动装配我们通常在配置文件中进行实现:一下代码就是手动装配: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="ht

  • Spring Boot如何动态创建Bean示例代码

    前言 本文主要给大家介绍了关于Spring Boot动态创建Bean的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. SpringBoot测试版本:1.3.4.RELEASE 参考代码如下: package com.spring.configuration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.su

  • Spring实战之Bean的作用域request用法分析

    本文实例讲述了Spring实战之Bean的作用域request用法.分享给大家供大家参考,具体如下: 一 配置 1 applicationContext.xml <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframewor

  • Spring实战之注入嵌套Bean操作示例

    本文实例讲述了Spring实战之注入嵌套Bean操作.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:sch

  • Spring实战之让Bean获取Spring容器操作示例

    本文实例讲述了Spring实战之让Bean获取Spring容器操作.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"

  • Java类获取Spring中bean的5种方式

    获取Spring中的bean有很多种方式,再次总结一下: 第一种:在初始化时保存ApplicationContext对象 ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml"); ac.getBean("beanId"); 说明:这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring. 第二种:通过Spring提供

  • 简单了解Spring Bean常用注解的装配

    这篇文章主要介绍了简单了解Spring Bean常用注解的装配,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 基于注解的装配 在Spring框架中,尽管使用XML配置文件可以很简单地装配Bean,但如果应用中有大量的Bean需要装配,会导致XML配置文件过于庞大,不方便以后的升级与维护,因此更多的时候推荐开发者使用注解(annotation)的方式去装配Bean. 在Spring框架中定义了一系列的注解,下面介绍集中常用的注解. @Compon

  • 详解Spring简单容器中的Bean基本加载过程

    本篇将对定义在 XMl 文件中的 bean,从静态的的定义到变成可以使用的对象的过程,即 bean 的加载和获取的过程进行一个整体的了解,不去深究,点到为止,只求对 Spring IOC 的实现过程有一个整体的感知,具体实现细节留到后面用针对性的篇章进行讲解. 首先我们来引入一个 Spring 入门使用示例,假设我们现在定义了一个类 org.zhenchao.framework.MyBean ,我们希望利用 Spring 来管理类对象,这里我们利用 Spring 经典的 XMl 配置文件形式进行

  • Spring Bean的初始化和销毁实例详解

    本文实例讲述了Spring Bean的初始化和销毁.分享给大家供大家参考,具体如下: 一 点睛 在开发过程中,经常遇到在Bean使用之前或者之后做一些必要的操作,Spring对Bean的生命周期的操作提供了支持. 1 Java配置方式:使用@Bean的initMethod和destroyMethod. 2 注解方式:利用JSR-250的@PostConstruct和@PreDestroy. 二 实战 1 增加JSR250支持 <dependency> <groupId>javax.

随机推荐