实战Spring自动化装配 bean

创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。

目录

 

Spring配置的可选方案

自动化装配bean

如何创建可被发现的bean

带 有 @Component注 解 的 CompactDisc实 现 类 SgtPeppers

@ComponentScan注解启用了组件扫描

通过XML启用组件扫描

测试组件扫描能够发现CompactDisc

重命名组件扫描的bean

设置组件扫描的基础包

通过设置包名

通过设置包中所包含的类或接口

通过为bean添加注解实现自动装配

通过自动装配,将一个CompactDisc注入到CDPlayer之中

验证自动装配

StandardOutputStreamLog

总结


Spring配置的可选方案

Spring 提供了三种主要的装配机制:

  • 在XML中进行显式配置
  • 在Java中进行显式配置
  • 隐式的bean发现机制和自动装配

以上方案根据自己喜好来选择即可,最主要的是适合自己的项目即可,当然可以混搭,你可以选择使用XML装配一些bean,使用Spring基于Java的配置(JavaConfig)来装配另一些bean,而将剩余的 bean让Spring去自动发现。

如果必须有优先级的话:

我的建议是尽可能地使用自动配置的机制。显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来维护 的,而当你需要为这些代码配置bean的时候),我推荐使用类型安全并且比XML更加强大的JavaConfig。最后,只有当你想要使用便利的XML 命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。

自动化装配bean

能用自动化装配,尽量用自动化装配。

Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。

如何创建可被发现的bean

以下例子用CD(compact disc)来举例,

首先在Java中建立CD的概念。程序清单2.1展现了CompactDisc,它是定义CD的一个接口:

package soundsystem;

public interface CompactDisc {
  void play();
}

作为接口,它定义了CD播放器对一盘CD所能进行的操作。它将CD 播放器的任意实现与CD本身的耦合降低到了最小的程度。然后在创建这个接口的实现:

带 有 @Component注 解 的 CompactDisc实 现 类 SgtPeppers

package soundsystem;

import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {

    private String title = "世间美好与你环环相扣";
    private String artist = "柏松";

    @Override
    public void play() {
        System.out.println("网易云正在播放:" + artist + "--" + title);
    }

}

这个类里面主要 使用了@Component注解。

这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。没有必要显式配置SgtPeppersbean,因为这个类使用 了@Component注解,已经通知到 Spring。

@Component 注解表示:

(把普通pojo实例化到spring容器中,相当于配置文件中的 
<bean id="" class=""/>

需要注意的是 组件扫描默认是不启用的。需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

@ComponentScan注解启用了组件扫描

@ComponentScan默认会扫描与配置类相同的包

package soundsystem;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * 类CDPlayerConfig通过Java代码定义了Spring的装配规则。
 *
 * @author imenger
 * @date 2021/2/23 5:45 下午
 */
@Configuration
@ComponentScan
public class CDPlayerConfig {
}

并没有显式地声明任何bean,只不过它使用了@ComponentScan注解,这个注解能够在Spring中启用组 件扫描。

CDPlayerConfig类会扫描当前包(soundsystem)以及所有子包下,带有@Component注解的类,这样的话,就能发现CompactDisc,并且会在Spring中自动为 其创建一个bean。

当然也可以用 xml 的配置方式来,效果与java bean 一致,以下是启用组件扫描的最简洁XML配置:

通过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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       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">

    <context:component-scan base-package="soundsystem"/>

</beans>

尽管我们可以通过XML的方案来启用组件扫描,但是在后面的讨论中,我更多的还是会使用基于Java的配置。如果你更喜欢XML的话,<context:component-scan>元素会有与@ComponentScan注解相对应的属性和子元素。不过 好多项目一般都是用 xml,配合@Component注解 为混搭来做的,维护起来不好维护。

接下来 做一下测试,为了测试组件扫描的功能,我们创建一个简单的JUnit测试,它会 创建Spring上下文,并判断CompactDisc是不是真的创建出来了。

测试组件扫描能够发现CompactDisc

package soundsystem;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertNotNull;

/**
 * 测试 通过 Spring 注解 创建可被发现的bean
 *
 * @author imenger
 * @date 2021/2/23 5:59 下午
 * <p>
 * Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。
 * 注解@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。
 * 因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDisc这个bean。
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayOneTest {

    @Autowired
    private CompactDisc cd;

    /**
     * cdShouldNotBeNull
     *
     * @param
     * @return void
     * 一个简单的测试方法断言cd属性不为null。
     * 如果它不为null的话,就意味着 Spring能够发现CompactDisc类,
     * 自动在Spring上下文中将其创建为bean并将其注入到测试代码之中。
     * @author imenger
     * @date 2021/2/23 6:12 下午
     */
    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

}

当然你也可以 cd.play(),输出结果:网易云正在播放:柏松--世间美好与你环环相扣

重命名组件扫描的bean

Spring应用上下文中所有的bean都会给定一个ID。在前面的例子中,尽管我们没有明确地为SgtPeppersbean设置ID,但Spring会根据类名为 其指定一个ID。具体来讲,这个bean所给定的ID为sgtPeppers,也就是将类名的第一个字母变为小写。其是就是:别名。

如果想为这个bean设置不同的别名,你所要做的就是将期望的ID作为值传递给@Component注解。比如说,如果想将这个bean标识 为“爱情错觉”,那么你需要将SgtPeppers类的@Component注解配置为如下所示:

@Component("loveIllusion")
public class SgtPeppers implements CompactDisc {
    ...
}

还有另外一种为bean命名的方式,这种方式不使用@Component注解,而是使用Java依赖注入规范(Java Dependency Injection)中所提供 的@Named注解来为bean设置ID:

@Named("loveIllusion")
public class SgtPeppers implements CompactDisc {
    ...
}

Spring支持将@Named作为@Component注解的替代方案。两者之间有一些细微的差异,但是在大多数场景中,它们是可以互相替换的。

话虽如此,我更加强烈地喜欢@Component注解,而对于@Named......怎么说呢,我感觉它的名字起得很不好。它并没有像@Component那样 清楚地表明它是做什么的。因此以后及其示例代码中,我不会再使用@Named。

设置组件扫描的基础包

@ComponentScan没有设置任何属性的情况下,默认会以配置类所在的包作为基础包(base package)来扫描组件。

通过设置包名

如果想要把 配置类单独放到另外包,或者 说 设置其他的包为组件扫描,可以在原有注解里设置对应包名的值:

@ComponentScan("soundsystem")  或者 @ComponentScan(basePackages = "soundsystem")

当然可以设置多个 包,用,分割即可,比如:

@ComponentScan(basePackages = {"soundsystem","soundsystem1"})

通过设置包中所包含的类或接口

@ComponentScan(basePackageClasses = {CDPlayer.class})
这个 CDPlayer 类所在的包将会作为组件扫描的基础包。

通过为bean添加注解实现自动装配

需要有一种方法能够将组件扫描得到的bean和它们的依赖装配在一 起,来完成这项任务就是自动装配。

简单来说,自动装配就是让Spring自动满足bean依赖的一种方法。为了声明要进行自动装配,我们可以借助Spring的@Autowired注解。

构造方法添加了@Autowired注解,这表明当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。

通过自动装配,将一个CompactDisc注入到CDPlayer之中

不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。

package soundsystem;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc cd;

    /**
     * CDPlayer
     *
     * @param cd
     * @return
     * @author imenger
     * @date 2021/2/24 4:26 下午
     * @Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。
     */
    @Autowired
    public CDPlayer(CompactDisc cd) {
        this.cd = cd;
    }

    @Override
    public void play() {
        cd.play();
    }

}

 与 @Autowired 注解相同用法的还有 @Inject,但是平时不怎么用。

验证自动装配

我们已经在CDPlayer的构造器中添加了@Autowired注解,Spring将把一个可分配给CompactDisc类型的bean自动注入进来。为了 验证这一点,让我们修改一下CDPlayerTest,使其能够借助CDPlayer bean播放我们的音乐:

package soundsystem;

import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {

    @Rule
    public final StandardOutputStreamLog log = new StandardOutputStreamLog();

    @Autowired
    private MediaPlayer player;

    @Autowired
    private CompactDisc cd;

    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

    @Test
    public void play() {
        player.play();
        assertEquals(
                "网易云正在播放:柏松--世间美好与你环环相扣\n",
                log.getLog());
    }

}

在play()测试方法中,我们可以调用CDPlayer的play()方法,并断言它的行为与你的预期一致。

StandardOutputStreamLog

如果直接用System.out.println(),是不是有点 low,所以咱们要多写一些看上去高大上,实际上用不到的代码,比如:StandardOutputStreamLog,这是来源于 System Rules库(http://stefanbirkner.github.io/system-rules/index.html)的一个JUnit规则,该规则能够基于控制台的输出编写断言。在这里,我 们断言SgtPeppers.play()方法的输出被发送到了控制台上。

总结

组件扫描,和自动装配,也就是 所谓的 @ComponentScan 和 @Autowired 就到这里,下一篇看看Spring中如何显式地装配bean,通过 java 代码的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值