zoukankan      html  css  js  c++  java
  • 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 chapter02;
    
    public interface CompactDisc {
        void play();
    }
    

    然后创建CD接口的一个实现类SgtPeppers:

    package chapter02;
    
    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。

    那么如何让Spring发现它并创建bean呢?

    这时就需要用到组件扫描,不过,在Spring中,组件扫描默认是不启用的

    因此我们需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

    创建CDPlayerConfig类:

    package chapter02;
    
    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包的方式如下:

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

    package chapter02;
    
    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注解,会将SgtPeppers bean注入到字段compactDisc上,因为它是接口CompactDisc的实现类并且添加了@Component注解。

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

    3. 设置bean ID

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

    如上面代码中SgtPeppers bean的ID为sgtPeppers。

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

    3.1 使用@Component设置bean ID

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

    3.2 使用@Named设置bean ID

    @Named注解不是Spring框架的注解,而是Java 依赖注入规范(Java Dependency Injection)中的注解,因此需要导入jar包:javax.inject-1.jar,导入jar包的方式可以参考 Spring入门(一):创建Spring项目

    import javax.inject.Named;
    
    @Named("lonelyHeartsClub")
    public class SgtPeppers implements CompactDisc {
    	......
    }
    

    在Spring项目中建议使用@Component注解。

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

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

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

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

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

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

    @ComponentScan("chapter02")
    public class CDPlayerConfig {
    }
    

    或者:

    @ComponentScan(basePackages = "chapter02")
    public class CDPlayerConfig {
    }
    

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

    @ComponentScan(basePackages = {"chapter01", "chapter02"})
    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 chapter02;
    
    public interface MediaPlayer {
        void play();
    }
    
    package chapter02;
    
    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 chapter02;
    
    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 chapter02;
    
    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 chapter02;
    
    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;
        }
    
        @Override
        public void play() {
            compactDisc.play();
        }
    }
    

    在Spring项目中建议使用@Autowired注解

    6. 验证自动装配

    修改CDPlayerTest类代码测试自动装配。

    package chapter02;
    
    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 mediaPlayer;
    
        @Autowired
        private CompactDisc compactDisc;
    
        @Test
        public void cdShouldNotBeNull() {
            assertNotNull(compactDisc);
            compactDisc.play();
        }
    
        @Test
        public void play() {
            mediaPlayer.play();
            assertEquals("Playing Sgt.Pepper's Lonely Hearts Club Band By The Beatles
    ", log.getLog());
        }
    }
    

    因为代码中使用了StandardOutputStreamLog类,因此需要导入jar包:system-rules-1.16.0.jar,导入jar包的方式可以参考 Spring入门(一):创建Spring项目

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

    7. 源码及参考

    源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

    Craig Walls 《Spring实战(第4版)》

    原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

    如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

  • 相关阅读:
    动画:UIViewAnimationOptions类型
    ReactiveCocoa--RACTuple
    RACSignal的一些常用用法
    神奇的RAC宏
    UITableViewStyleGrouped模式下多余间距
    UITableViewStyleGrouped模式下烦人的多余间距
    上传到 App Store 时出错。
    [iOS]详解调整UIButton的title和image的位置
    规范化目录

  • 原文地址:https://www.cnblogs.com/zwwhnly/p/10478615.html
Copyright © 2011-2022 走看看