欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring入门(二):自动化装配bean

程序员文章站 2022-04-08 19:53:39
Spring从两个角度来实现自动化装配: 组件扫描(component scanning):Spring会自动发现应用上下文中需要创建的bean。 自动装配(autowiring):Spring会自动满足bean之间的依赖。 为了更形象的解释组件扫描与自动装配,我们举一个音响系统的例子,主要包含以下 ......

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

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

为了更形象的解释组件扫描与自动装配,我们举一个音响系统的例子,主要包含以下内容:

  • cd接口
  • cd接口的一个实现类
  • cd播放器

关于cd和cd播放器关系的解释:

如果你不将cd插入(注入)到cd播放器中,那么cd播放器其实是没有太大用处的。所以,可以这样说,

cd播放器依赖于cd才能完成它的使命。

1.创建可被发现的bean

先创建cd接口compactdisc:

package soundsystem;

public interface compactdisc {
    void play();
}

然后创建cd接口的一个实现类sgtpeppers:

package soundsystem;

import org.springframework.stereotype.component;

@component
public class sgtpeppers implements compactdisc {

    @override
    public void play() {
        string title = "sgt.pepper's lonely hearts club band ";
        string artists = "the beatles";
        system.out.println("playing " + title + " by " + artists);
    }
}

sgtpeppers类与以往类的区别在于使用了@component注解。这个注解表明该类会作为组件类,并告知spring要为这个类创建bean。

创建了bean,那么如何让spring发现它呢?这时就需要用到组件扫描,不过,在spring中,组件扫描默认是不启用的。因此我们需要显式配置一下spring,从而命令它去寻找带有@component注解的类,并为其创建bean。

创建cdplayerconfig类:

package soundsystem;

import org.springframework.context.annotation.componentscan;

@componentscan
public class cdplayerconfig {
}

这个类与以往类的区别是使用了@componentscan注解,这个注解能够spring中启用组件扫描。

@componentscan默认会扫描与配置类相同的包以及这个包下的所有子包,查找带有@component注解的类。

2.验证组件扫描

为了验证创建的bean能否被spring发现,我们创建一个简单的junit测试,完成此测试需要导入以下两个jar包:

  • hamcrest-core-2.1.jar
  • junit-4.12.jar

导入jar包的方式如下:

Spring入门(二):自动化装配bean

Spring入门(二):自动化装配bean

Spring入门(二):自动化装配bean

Spring入门(二):自动化装配bean

Spring入门(二):自动化装配bean

导入完成后的项目结构图如下所示:

Spring入门(二):自动化装配bean

package soundsystem;

import static org.junit.assert.*;

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;

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes = cdplayerconfig.class)
public class cdplayertest {

    @autowired
    private compactdisc compactdisc;

    @test
    public void cdshouldnotbenull() {
        assertnotnull(compactdisc);
        compactdisc.play();
    }
}

代码简单讲解:

@runwith(springjunit4classrunner.class),会在测试开始的时候自动创建spring的应用上下文。

@contextconfiguration(classes = cdplayerconfig.class)会告诉spring需要在cdplayerconfig中加载配置。

字段compactdisc上的@autowired注解,会将compactdisc bean(其实是sgtpeppers)注入到测试代码之中。

运行测试方法cdshouldnotbenull,会发现测试通过,compactdisc不为null:

Spring入门(二):自动化装配bean

3.为组件扫描的bean命名

spring应用上下文中所有的bean都会给定一个id,默认情况下,spring会将类名的第一个字母变为小写,作为该bean的id。

如上面代码中sgtpeppers bean的id为sgtpeppers。

有以下两种方式来设置bean id:

方式1:使用@component设置bean id

@component("lonelyheartsclub")
public class sgtpeppers implements compactdisc {
    ...
}

方式2:使用@named设置bean id

@named注解不是spring框架的注解,而是java 依赖注入规范(java dependency injection)中的注解,因此需要导入jar包:javax.inject-1.jar。

package soundsystem;

import javax.inject.named;

@named("lonelyheartsclub")
public class sgtpeppers implements compactdisc {
    ...
}

在spring项目中建议使用@component注解。

4.设置组件扫描的基础包

按照默认规则 ,@componentscan注解会以配置类所在的包作为基础包(base package)来扫描组件。

但有时候,我们会将配置类放在单独的包中,使其与其他的应用代码区分开来。

这种场景下,默认的基础包就满足不了需求。

@componentscan注解支持传入指定的基础包,有以下几种场景:

4.1 指定要扫描的基础包(单个)

@componentscan("soundsystem")
public class cdplayerconfig {
}

或者:

@componentscan(basepackages = "soundsystem")
public class cdplayerconfig {
}

4.2 指定要扫描的基础包(多个)

@componentscan(basepackages = {"soundsystem", "video"})
public class cdplayerconfig {
}

4.3 指定要扫描的基础包(类型安全)

@componentscan(basepackageclasses = {cdplayer.class})
public class cdplayerconfig {
}

如上所示,basepackageclasses也支持指定多个类,指定类所在的包将会作为组件扫描的基础包。

建议使用这种类型安全方式来指定扫描的基础包。

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

自动装配是让spring自动满足bean 依赖的一种方法,在满足依赖的过程中,会在spring应用上下文中寻找匹配某个bean需要的其他bean。

实现自动装配,需要使用spring的@autowired注解。

@autowired一般情况下,有以下3种使用方式:

5.1 使用在构造器上

package soundsystem;

public interface mediaplayer {
    void play();
}
package soundsystem;

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;

@component
public class cdplayer implements mediaplayer {

    private compactdisc compactdisc;

    @autowired
    public cdplayer(compactdisc compactdisc) {
        this.compactdisc = compactdisc;
    }

    @override
    public void play() {
        compactdisc.play();
    }
}

5.2 使用在属性的setter方法上

package soundsystem;

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;

@component
public class cdplayer implements mediaplayer {

    private compactdisc compactdisc;

    @autowired
    public void setcompactdisc(compactdisc compactdisc) {
        this.compactdisc = compactdisc;
    }

    @override
    public void play() {
        compactdisc.play();
    }
}

5.3 使用在类的任何方法上

package soundsystem;

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;

@component
public class cdplayer implements mediaplayer {

    private compactdisc compactdisc;

    @autowired
    public void insertdisc(compactdisc compactdisc) {
        this.compactdisc = compactdisc;
    }

    @override
    public void play() {
        compactdisc.play();
    }
}

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

假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。

如果没有匹配的bean,那么在应用上下文创建的时候,spring会抛出一个异常。

可以通过设置require属性为false避免该异常出现:

@autowired(required = false)
public cdplayer(compactdisc compactdisc) {
    this.compactdisc = compactdisc;
}

不过建议谨慎使用,避免未找到bean进行匹配,而且代码没有进行null检查而出现nullpointerexception。

如果有多个bean都能满足依赖关系的话,spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配。

@autowired注解也可以替换成@inject注解(来源于java依赖注入规范),同样可以实现自动装配:

package soundsystem;

import org.springframework.stereotype.component;
import javax.inject.inject;

@component
public class cdplayer implements mediaplayer {

    private compactdisc compactdisc;

    @inject
    public cdplayer(compactdisc compactdisc) {
        this.compactdisc = compactdisc;
    }

    ...
}

在spring项目中建议使用@inject注解。

6.验证自动装配

修改cdplayertest类代码测试自动装配

package soundsystem;

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;

import static org.junit.assert.assertequals;
import static org.junit.assert.assertnotnull;

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes = cdplayerconfig.class)
public class cdplayertest {

    @rule
    public final standardoutputstreamlog log = new standardoutputstreamlog();

    @autowired
    private mediaplayer player;

    @autowired
    private compactdisc compactdisc;

    @test
    public void cdshouldnotbenull() {
        assertnotnull(compactdisc);
        compactdisc.play();
    }

    @test
    public void play() {
        player.play();
        assertequals("playing sgt.pepper's lonely hearts club band by the beatles\r\n", log.getlog());
    }
}

因为代码中使用了standardoutputstreamlog类,因此需要导入jar包:system-rules-1.16.0.jar

运行测试方法play(),输出内容和预期一致,说明字段player已经被mediaplayer的实现类cdplayer bean装配,测试通过,如下所示:

Spring入门(二):自动化装配bean

7.源码地址

https://github.com/zwwhnly/springstudydemo.git,欢迎大家下载,有问题可以多多交流。