Spring 教程

Spring 自动装配

什么是自动装配?

Spring 容器可以自动装配 Bean 之间的关系。你可以让 Spring 通过检查 ApplicationContext 的内容来自动为您的 bean 解析依赖者(其他 bean)。自动装配具有以下优点:

(1)自动装配可以大大减少指定属性或构造函数参数的需要。

(2)自动装配可以随着对象的发展而更新配置。例如,如果您需要向类中添加依赖项,则无需修改配置即可自动满足该依赖项。因此,自动装配在开发过程中特别有用,当代码库变得更加稳定时,不排除切换到显式装配的选择。

四种装配模式

用基于 XML 的配置元数据时,您可以使用 <bean/> 元素的 autowire 属性为 bean 定义指定自动装配模式。自动装配功能有四种模式。您可以为每个 Bean 指定自动装配模式,从而选择要自动装配的 Bean。下面描述了四种自动装配模式:

no(默认)

不自动装配。Bean 引用必须由 ref 元素定义。对于大型部署,不建议更改默认设置,因为明确指定依赖会带来更强的控制力和清晰度。在某种程度上,它记录了系统的结构。

byName

根据属性名称自动装配。Spring 会查找与需要自动装配的属性名称相同的 Bean。例如,如果一个 Bean 定义被设置为按名称自动装配,并且它包含一个 master 属性(即它有一个 setMaster(..) 方法),那么 Spring 就会查找一个名为 master 的 Bean 定义,并使用它来设置该属性。

byType

如果果容器中正好只存在一个属性类型的 Bean,则允许对该属性进行自动装配。如果存在一个以上的 Bean,则会抛出一个异常,表示不能对该 Bean 使用 byType 自动装配。如果没有匹配的 Bean,则不会发生任何事情(不设置属性)。

constructor

与 byType 类似,但适用于构造函数参数。如果容器中没有一个构造函数参数类型的 Bean,就会出现错误。

注意

使用 byType 或 constructor 自动装配模式时,可以装配数组和类型集合。在这种情况下,容器中所有符合预期类型的自动装配候选对象都会提供,以满足依赖关系。如果预期键类型是字符串,则可以自动装配强类型的 Map 实例。自动装配的 Map 实例的值由符合预期类型的所有 Bean 实例组成,而 Map 实例的键则包含相应的 Bean 名称。

自动装配示例

applicationContext-ioc-demo5.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.xsd">

   <bean id="beanTwo" class="com.hxstrive.spring5.ioc.demo5.BeanTwo" />
   <bean id="beanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree">
       <constructor-arg name="name" value="hello world" />
   </bean>
   
   <bean id="beanOne" class="com.hxstrive.spring5.ioc.demo5.BeanOne" autowire="byType" />
</beans>

BeanOne.java:

package com.hxstrive.spring5.ioc.demo5;

import java.util.Arrays;
import java.util.Map;

public class BeanOne {
   private BeanTwo beanTwo;
   private BeanThree beanThree;

   private BeanTwo[] beanTwos;

   private Map<String,BeanThree> beanThreeMap;


   @Override
   public String toString() {
       StringBuilder builder = new StringBuilder();
       builder.append("beanTwo=" + beanTwo);
       builder.append("\nbeanThree=" + beanThree);
       builder.append("\nbeanTwos=" + Arrays.toString(beanTwos));
       builder.append("\nbeanThreeMap=" + beanThreeMap);
       return builder.toString();
   }

   // 省略 getter 和 setter 方法
}

BeanTwo.java:

package com.hxstrive.spring5.ioc.demo5;

public class BeanTwo {

   @Override
   public String toString() {
       return "BeanTwo{}";
   }

}

BeanThree.java:

package com.hxstrive.spring5.ioc.demo5;

public class BeanThree {
   private String name;

   public BeanThree(String name) {
       this.name = name;
   }

   @Override
   public String toString() {
       return "BeanThree{" +
               "name='" + name + '\'' +
               '}';
   }

}

Main.java:

package com.hxstrive.spring5.ioc.demo5;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* Bean 自动装配
* @author hxstrive.com
*/
public class Main {

   public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-ioc-demo5.xml");
       BeanOne beanOne = context.getBean(BeanOne.class);
       System.out.println(beanOne.toString());
   }

}

运行示例,输出如下:

beanTwo=BeanTwo{}
beanThree=BeanThree{name='hello world'}
beanTwos=[BeanTwo{}]
beanThreeMap={beanThree=BeanThree{name='hello world'}}

自动装配的局限性和缺点

装配在整个项目中一致使用时效果最好。如果通常不使用自动装配,那么仅使用它来连接一个或两个 bean 定义可能会使开发人员感到困惑。

考虑自动装配的局限性和缺点:

  • 属性和构造函数参数设置中的显式依赖关系总是覆盖自动装配。您不能自动装配简单的属性,如原始类型、字符串和类 (以及这些简单属性的数组)。这种限制是设计所致。

  • 自动装配不如显式装配精确。不过,正如前面所指出的,Spring 会小心地避免猜测,以免歧义可能产生意想不到的结果。不再显式地记录 Spring 管理的对象之间的关系。

  • Spring 容器生成文档的工具可能无法获得连接信息。

  • 容器中的多个 Bean 定义可能与 setter 方法或构造器参数指定的类型相匹配,从而自动装配。对于数组、集合或 Map 实例,这不一定是个问题。但是,对于预期只有一个值的依赖关系,这种模糊性就不能任意解决了。如果没有可用的唯一 bean 定义,就会出现异常。

一种情况下,您有几种选择:

  • 放弃自动装配,转而使用显式装配。

  • 通过将 bean 定义的 autowire-candidate 属性设置为 false,可以避免为 bean 定义自动布线。例如:

<?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.xsd">

   <bean id="beanTwo" class="com.hxstrive.spring5.ioc.demo5.BeanTwo" />
   <bean id="beanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree">
       <constructor-arg name="name" value="hello world" />
   </bean>

   <!-- 禁止该 bean 被自动装配 -->
   <bean id="beanThree2" class="com.hxstrive.spring5.ioc.demo5.BeanThree" autowire-candidate="false">
       <constructor-arg name="name" value="hi! spring" />
   </bean>

   <bean id="beanOne" class="com.hxstrive.spring5.ioc.demo5.BeanOne" autowire="byType" />

</beans>

运行示例,输出如下:

beanTwo=BeanTwo{}
beanThree=BeanThree{name='hello world'}
beanTwos=[BeanTwo{}]
beanThreeMap={beanThree=BeanThree{name='hello world'}}
  • 通过将 <bean/> 元素的 primary 属性设置为 true,将单个 Bean 定义指定为主要候选。例如:

<?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.xsd">

   <bean id="beanTwo" class="com.hxstrive.spring5.ioc.demo5.BeanTwo" />
   <bean id="beanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree">
       <constructor-arg name="name" value="hello world" />
   </bean>

   <!-- 禁止该 bean 被自动装配 -->
   <bean id="beanThree2" class="com.hxstrive.spring5.ioc.demo5.BeanThree" autowire-candidate="false">
       <constructor-arg name="name" value="hi! spring" />
   </bean>

   <bean id="primaryBeanThree" class="com.hxstrive.spring5.ioc.demo5.BeanThree" primary="true">
       <constructor-arg name="name" value="primary beanThree" />
   </bean>

   <bean id="beanOne" class="com.hxstrive.spring5.ioc.demo5.BeanOne" autowire="byType" />

</beans>

运行示例,输出如下:

beanTwo=BeanTwo{}
beanThree=BeanThree{name='primary beanThree'}
beanTwos=[BeanTwo{}]
beanThreeMap={beanThree=BeanThree{name='hello world'}, primaryBeanThree=BeanThree{name='primary beanThree'}}
  • 通过基于注释的配置实现更精细的控制。

将 Bean 排除在自动装配之外

根据每个 Bean 的情况,您可以将某个 Bean 排除在自动装配之外。在 Spring 的 XML 格式中,将 <bean/> 元素的 autowire-candidate 属性设置为 false。Spring 容器会使该 Bean 定义对自动装配基础架构不可用(包括注解样式配置,如 @Autowired)。

注意:autowire-candidate 属性只影响基于类型(byType)的自动装配。它不会影响通过名称(byName)进行的显式引用,即使指定的 Bean 没有被标记为自动装配候选对象,这种引用也会被解析。因此,如果名称匹配,按名称自动装配仍会注入一个 Bean。

您还可以根据与 Bean 名称的模式匹配来限制自动装配候选对象。顶层 <beans/> 元素的 default-autowire-candidates 属性可接受一个或多个模式。例如,要将自动装配候选状态限制为名称以 Repository 结尾的任何 Bean,可提供 *Repository 值。要提供多个模式,请用逗号分隔的列表定义它们。Bean 定义的 autowire-candidate 属性的显式值 true 或 false 始终优先。对于此类 Bean,模式匹配规则并不适用。

对于您永远不想通过自动装配注入到其他 Bean 中的 Bean,这些技术非常有用。这并不意味着被排除的 Bean 本身不能使用自动装配进行配置。相反,Bean 本身并不是自动装配其他 Bean 的候选对象。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号