彻底搞明白Spring中的自动装配和Autowired

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底搞明白Spring中的自动装配和Autowired相关的知识,希望对你有一定的参考价值。

参考技术A

当Spring装配Bean属性时,有时候非常明确,就是需要将某个Bean的引用装配给指定属性。比如,如果我们的应用上下文中只有一个 org.mybatis.spring.SqlSessionFactoryBean 类型的Bean,那么任意一个依赖 SqlSessionFactoryBean 的其他Bean就是需要这个Bean。毕竟这里只有一个 SqlSessionFactoryBean 的Bean。

为了应对这种明确的装配场景,Spring提供了自动装配(autowiring)。与其显式的装配Bean属性,为何不让Spring识别出可以自动装配的场景。

当涉及到自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种自动装配策略。

Spring在 AutowireCapableBeanFactory 接口中定义了这几种策略。其中, AUTOWIRE_AUTODETECT 被标记为过时方法,在Spring3.0之后已经不再支持。

它的意思是,把与Bean的属性具有相同名字的其他Bean自动装配到Bean的对应属性中。听起来可能比较拗口,我们来看个例子。

首先,在User的Bean中有个属性 Role myRole ,再创建一个Role的Bean,它的名字如果叫myRole,那么在User中就可以使用byName来自动装配。

上面是Bean的定义,再看配置文件。

如上所述,只要属性名称和Bean的名称可以对应,那么在user的Bean中就可以使用byName来自动装配。那么,如果属性名称对应不上呢?

是的,如果不使用属性名称来对应,你也可以选择使用类型来自动装配。它的意思是,把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。

还是上面的例子,如果使用byType,Role Bean的ID都可以省去。

它是说,把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。值的注意的是, 具有相同类型的其他Bean 这句话说明它在查找入参的时候,还是通过Bean的类型来确定。

构造器中入参的类型为Role

它首先会尝试使用constructor进行自动装配,如果失败再尝试使用byType。不过,它在Spring3.0之后已经被标记为 @Deprecated 。

默认情况下,default-autowire属性被设置为none,标示所有的Bean都不使用自动装配,除非Bean上配置了autowire属性。
如果你需要为所有的Bean配置相同的autowire属性,有个办法可以简化这一操作。
在根元素Beans上增加属性 default-autowire="byType" 。

Spring自动装配的优点不言而喻。但是事实上,在Spring XML配置文件里的自动装配并不推荐使用,其中笔者认为最大的缺点在于不确定性。或者除非你对整个Spring应用中的所有Bean的情况了如指掌,不然随着Bean的增多和关系复杂度的上升,情况可能会很糟糕。

从Spring2.5开始,开始支持使用注解来自动装配Bean的属性。它允许更细粒度的自动装配,我们可以选择性的标注某一个属性来对其应用自动装配。

Spring支持几种不同的应用于自动装配的注解。

我们今天只重点关注Autowired注解,关于它的解析和注入过程,请参考笔者Spring源码系列的文章。 Spring源码分析(二)bean的实例化和IOC依赖注入

使用@Autowired很简单,在需要注入的属性加入注解即可。

不过,使用它有几个点需要注意。

默认情况下,它具有强制契约特性,其所标注的属性必须是可装配的。如果没有Bean可以装配到Autowired所标注的属性或参数中,那么你会看到 NoSuchBeanDefinitionException 的异常信息。

看到上面的源码,我们可以得到这一信息,Bean集合为空不要紧,关键 isRequired 条件不能成立,那么,如果我们不确定属性是否可以装配,可以这样来使用Autowired。

我记得曾经有个面试题是这样问的:Autowired是按照什么策略来自动装配的呢?

关于这个问题,不能一概而论,你不能简单的说按照类型或者按照名称。但可以确定的一点的是,它默认是按照类型来自动装配的,即byType。

关键点 findAutowireCandidates 这个方法。

可以看到它返回的是一个列表,那么就表明,按照类型匹配可能会查询到多个实例。到底应该装配哪个实例呢?我看有的文章里说,可以加注解以此规避。比如 @qulifier、@Primary 等,实际还有个简单的办法。

比如,按照UserService接口类型来装配它的实现类。UserService接口有多个实现类,分为 UserServiceImpl、UserServiceImpl2 。那么我们在注入的时候,就可以把属性名称定义为Bean实现类的名称。

这样的话,Spring会按照byName来进行装配。首先,如果查到类型的多个实例,Spring已经做了判断。

可以看出,如果查到多个实例, determineAutowireCandidate 方法就是关键。它来确定一个合适的Bean返回。其中一部分就是按照Bean的名称来匹配。

最后我们回到问题上,得到的答案就是:@Autowired默认使用byType来装配属性,如果匹配到类型的多个实例,再通过byName来确定Bean。

上面我们已经看到了,通过byType可能会找到多个实例的Bean。然后再通过byName来确定一个合适的Bean,如果通过名称也确定不了呢?
还是 determineAutowireCandidate 这个方法,它还有两种方式来确定。

它的作用是看Bean上是否包含@Primary注解,如果包含就返回。当然了,你不能把多个Bean都设置为@Primary,不然你会得到 NoUniqueBeanDefinitionException 这个异常。

你也可以在Bean上配置@Priority注解,它有个int类型的属性value,可以配置优先级大小。数字越小的,就被优先匹配。同样的,你也不能把多个Bean的优先级配置成相同大小的数值,否则 NoUniqueBeanDefinitionException 异常照样出来找你。

最后,有一点需要注意。Priority的包在 javax.annotation.Priority; ,如果想使用它还要引入一个坐标。

本章节重点阐述了Spring中的自动装配的几种策略,又通过源码分析了Autowired注解的使用方式。
在Spring3.0之后,有效的自动装配策略分为 byType、byName、constructor 三种方式。注解Autowired默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配,如果通过byName也确定不了,可以通过Primary和Priority注解来确定。

Spring入门

文章目录

Spring Beans 自动装配

  1. 可以使用·bean元素来声明 bean 和通过使用 XML 配置文件中的<constructor-arg>和<property>元素来注入 。
  2. Spring容器可以在不使用和 元素的情况下自动装配相互协作的 bean 之间的关系,这会减少XML配置的数量
  3. 自动装配模式 Spring 容器为来使用自动装配进行依赖注入
  4. 可以使用bean元素的autowire属性为一个bean定义指定自动装配模式
  5. bean的装配模式有以下几种
模式描述
no默认的设置,没有自动装配
byName由属性名自动装配,Spring容器在看到XML配置文件中bean的自动装配的属性设置为byName,然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。
byType由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。
constructor类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。
autodetectSpring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

Spring自动装配的局限性

  1. 当自动装配始终在同一个项目中使用时,效果是最好的
  2. 如果不是使用自动装配,可能会使我们使用它来连接只有一个或者两个bean定义
  3. 自动装配可以显著减少需要指定的属性或者构造器参数
限制描述
重写的可能性可以使用总是重写自动装配的 和 设置来指定依赖关系。
原始数据类型不能自动装配所谓的简单类型包括基本类型,字符串和类。
混乱的本质自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。

Spring 自动装配byName

  1. byName模式由属性名称指定自动装配。Spring容器看作beans,在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName,然后,它尝试将它发属性和配置文件定义为相同命名称的beans进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
  2. 在配置文件中,如果一个Bean定义设置为自动装配byName,并且它包含 spellChecker 属性(即,它有一个 setSpellChecker(…) 方法),那么 Spring 就会查找定义名为 spellChecker 的 bean,并且用它来设置这个属性。而且可以使用·<property> 标签连接其余的属性

Spring 自动装配byName样例

  1. 创建一个名称为 SpringExample 的项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint。
  2. 使用 Add External JARs 选项,添加所需的 Spring 库,
  3. 在 com.tutorialspoint 包中创建 Java 类 TextEditor,SpellChecker 和 MainApp
  4. 在 src 文件夹中创建 Beans 的配置文件 Beans.xml。

TextEditor.java 文件的内容:

package com.tutorialspoint;
public class TextEditor 
   private SpellChecker spellChecker;
   private String name;
   public void setSpellChecker( SpellChecker spellChecker )
      this.spellChecker = spellChecker;
   
   public SpellChecker getSpellChecker() 
      return spellChecker;
   
   public void setName(String name) 
      this.name = name;
   
   public String getName() 
      return name;
   
   public void spellCheck() 
      spellChecker.checkSpelling();
   
  

依赖类文件 SpellChecker.java 的内容:

package com.tutorialspoint;
public class SpellChecker 
   public SpellChecker() 
      System.out.println("Inside SpellChecker constructor." );
   
   public void checkSpelling() 
      System.out.println("Inside checkSpelling." );
      

MainApp.java 文件的内容:

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp 
   public static void main(String[] args) 
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      TextEditor te = (TextEditor) context.getBean("textEditor");
      te.spellCheck();
   

不适用自动装配byName的配置文件 Beans.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
       <property name="spellChecker" ref="spellChecker" />
       <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>

使用自动装配 “byName”的 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor" 
      autowire="byName">
      <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>

运行该应用程序,输出结果为

Inside SpellChecker constructor.
Inside checkSpelling.

Spring自动装配byType

  1. byType模式由属性类型指定自动装配,Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性,如果找到匹配项,它将注入这些 beans,否则,它将抛出异常
  2. 在配置文件中,如果一个 bean 定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellChecker 属性,那么 Spring 就会查找类型为 SpellChecker 的 bean并且用它来设置这个属性,仍然可以使用 标签连接其余属性。

Spring自动装配byType样例

  1. 创建一个名称为 SpringExample 的项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint。
  2. 使用 Add External JARs 选项,添加所需的 Spring 库,
  3. 在 com.tutorialspoint 包中创建 Java 类 TextEditor,SpellChecker 和 MainApp。
  4. 在 src 文件夹中创建 Beans 的配置文件 Beans.xml

TextEditor.java 文件的内容:

package com.tutorialspoint;
public class TextEditor 
   private SpellChecker spellChecker;
   private String name;
   public void setSpellChecker( SpellChecker spellChecker ) 
      this.spellChecker = spellChecker;
   
   public SpellChecker getSpellChecker() 
      return spellChecker;
   
   public void setName(String name) 
      this.name = name;
   
   public String getName() 
      return name;
   
   public void spellCheck() 
      spellChecker.checkSpelling();
   

``
**SpellChecker.java 的内容:**

```c
package com.tutorialspoint;
public class SpellChecker 
   public SpellChecker()
      System.out.println("Inside SpellChecker constructor." );
   
   public void checkSpelling() 
      System.out.println("Inside checkSpelling." );
      

MainApp.java 文件的内容:

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp 
   public static void main(String[] args) 
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      TextEditor te = (TextEditor) context.getBean("textEditor");
      te.spellCheck();
   

正常情况下的配置文件 Beans.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
      <property name="spellChecker" ref="spellChecker" />
      <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>

使用自动装配 “byType”,那么你的 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor" 
      autowire="byType">
      <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>

Spring 由构造函数自动装配

  1. 由构造函数自动装配模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常
  2. 在配置文件中,如果一个 bean 定义设置为通过构造函数自动装配,而且它有一个带有 SpellChecker 类型的参数之一的构造函数,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并用它来设置构造函数的参数。但是仍然可以使用 <constructor-arg> 标签连接其余属性。

Spring 由构造函数自动装配

  1. 创建一个名称为 SpringExample 的项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint。
  2. 使用 Add External JARs 选项,添加所需的 Spring 库,
  3. 在 com.tutorialspoint 包中创建 Java 类 TextEditor,SpellChecker 和 MainApp。
  4. 在 src 文件夹中创建 Beans 的配置文件 Beans.xml

TextEditor.java 文件的内容:

package com.tutorialspoint;
public class TextEditor 
   private SpellChecker spellChecker;
   private String name;
   public TextEditor( SpellChecker spellChecker, String name ) 
      this.spellChecker = spellChecker;
      this.name = name;
   
   public SpellChecker getSpellChecker() 
      return spellChecker;
   
   public String getName() 
      return name;
   
   public void spellCheck() 
      spellChecker.checkSpelling();
   

依赖类文件 SpellChecker.java 的内容:

package com.tutorialspoint;
public class SpellChecker 
   public SpellChecker()
      System.out.println("Inside SpellChecker constructor." );
   
   public void checkSpelling()
   
      System.out.println("Inside checkSpelling." );
     

MainApp.java 文件的内容

package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp 
   public static void main(String[] args) 
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      TextEditor te = (TextEditor) context.getBean("textEditor");
      te.spellCheck();
   

正常情况下的配置文件 Beans.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
      <constructor-arg  ref="spellChecker" />
      <constructor-arg  value="Generic Text Editor"/>
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>

使用自动装配 “by constructor”,的 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor" 
      autowire="constructor">
      <constructor-arg value="Generic Text Editor"/>
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>
高性能云服务器 精品线路独享带宽,毫秒延迟,年中盛惠 1 折起

以上是关于彻底搞明白Spring中的自动装配和Autowired的主要内容,如果未能解决你的问题,请参考以下文章

Spring 基于自动装配的依赖注入详解 [自动装配模式搞不懂?来看就完了!!!][autowire 属性值详解][自动装配的 6 种模式]

10分钟学Spring:一文搞懂spring依赖注入(DI)

彻底搞明白PHP中的include和require

彻底搞明白PHP的中引用的概念

彻底搞明白PHP的中引用的概念

Spring