• Spring实战之Bean的主要装配机制之一-组件扫描、自动装配bean



    Spring实战之容器、上下文、Bean的生命周期一文中,我们了解到Spring容器负责创建应用程序中的bean并通过DI来协调这些的对象之间的关系。但是,做为开发人员,我们需要告诉Spring要创建哪些bean并且如何将其装配在一起。当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:

    • 在XML中进行显式配置

    • 在Java中进行显式配置

    • 隐式的bean发现机制和自动装配

      本文主要讲解bean的自动装配
      
      • 1

    组件扫描-自动装配bean

    package com.demo;
    /**
     * CD接口定义了CD的概念
     */
    public interface CompactDisc {
        /**
         * CD可以被播放器播放
         */
        void play();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    package com.demo;
    
    import org.springframework.stereotype.Component;
    
    /**
     * 定义了一个具体的CD实现类
     *
     * @Component 注解表名该类会作为组件类,并告知Spring要为这个类创建bean。
     * 不过,组件扫描由于组件扫描默认是不启用的,所以我们还需要显示的配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。
     */
    @Component
    public class Fantasy implements CompactDisc {
    
        private String title = "双截棍";
        private String artist = "周杰伦";
    
        @Override
        public void play() {
            System.out.println("正在播放" + artist + " 的 " + title);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    package com.demo;
    
    import org.springframework.context.annotation.ComponentScan;
    
    /**
     * @ComponentScan 注解能够在Spring中启用组件扫描。
     * 如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包及其子包,查找带有@Component注解的类。
     * 这样会在Spring中自动为这些类创建一个bean
     */
    @Configuration
    @ComponentScan
    public class CDPlayerConfig {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package com.demo;
    
    import 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 cd;
    
        @Test
        public void cdShouldNotBeNull(){
            Assert.assertNotNull(cd);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    CDPlayerTest使用了Spring的 SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDisc bean。

    组件扫描-还能做什么?

    为组件扫描的bean命名

    Spring应用上下文中所有的bean都会给定一个ID。上面的Fantasy 类中,Spring会根据类名为其指定一个ID,为fantasy,也就是将类名的第一个字母变为小写。
    如果,想为这个bean设置不同的ID,可以在注解上写上属性做为bean的名称,如下:

    @Component("双截棍")
    public class Fantasy implements CompactDisc {
    ...
    }
    
    • 1
    • 2
    • 3
    • 4

    除了@Component注解外,还可以用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解来为bean设置ID,如下:

    @Named("双截棍")
    public class Fantasy implements CompactDisc {
    ...
    }
    
    • 1
    • 2
    • 3
    • 4

    设置组件扫描的基础包

    目前,以上代码中的@ComponentScan注解还没有设置任何属性,它将会以配置类所在的包做为基础包来扫描组件。

    指定不同的基础包扫描

    我们还可以设置属性来指定不同的基础包去扫描,如下:

    @Configuration
    @ComponentScan("com")
    public class CDPlayerConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    如果想更加清晰地表明我们设置的是基础包,可以通过basePackages属性进行配置,如下:

    @Configuration
    @ComponentScan(basePackages = "com")
    public class CDPlayerConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    设置多个基础包

    还可以设置多个基础包,如下:

    @Configuration
    @ComponentScan(basePackages = {"com","com1","com2"})
    public class CDPlayerConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    指定具体的类或接口所在包

    由于我们是通过String来设置的基础包,如果重构代码的话,可能所指定的基础包就会出错误了。所以,这个注解可以通过指定某个包所包含的类或接口的形式,来避免上述问题。如下:

    @Configuration
    @ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class})
    public class CDPlayerConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    上面basePackageClasses 属性中的设置的数组包含了类,这些类所在的包将会做为组件扫描的基础包。

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

    自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在应用上下文中寻找匹配某个bean需要的其他bean。
    我们可以通过Spring的@Autowired注解。

    类构造器上添加@Autowired注解

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

    @Component
    public class CDPlayer implements MediaPlayer{
        private CompactDisc cd;
    
        @Autowired
        public CDPlayer(CompactDisc cd) {
            this.cd = cd;
        }
        
        public void play(){
            cd.play();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Setter方法上添加@Autowired注解

    如下:

    	@Autowired
        public void setCd(CompactDisc cd) {
            this.cd = cd;
        }
    
    • 1
    • 2
    • 3
    • 4

    任意方法上添加@Autowired注解

    如下:

    	@Autowired
        public void insertCd(CompactDisc cd) {
            this.cd = cd;
        }
    
    • 1
    • 2
    • 3
    • 4

    如上,不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。加入有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。

    @Autowired的属性设置为false

    如果没有匹配的bean,那么在应用上下文创建的时候,Spring会跑出一个异常。为了避免异常的出现,可以将@Autowired的required属性设置为false,如下:

        @Autowired(required = false)
        public CDPlayer(CompactDisc cd) {
            this.cd = cd;
        }
    
    • 1
    • 2
    • 3
    • 4

    此时,Spring将会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。所以这种情况下,如果代码中没有对null检查的话,可能会出现NPE。

    如果有多个bean都能满足依赖关系的话,Spring会抛出异常。

    @Inject注解

    同@Named注解一样,@Inject注解也来源与Java依赖注入规范。在自动装配中,Spring同时支持@Inject和@Autowired。如下:

    @Named
    public class CDPlayer2 {
        private CompactDisc cd;
    
        @Inject
        public CDPlayer2(CompactDisc cd) {
            this.cd = cd;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    nginx网络服务配置
    数据结构之折半插入排序
    用python写MapReduce函数——以WordCount为例
    百度地图个性化地图失效问题
    【我的渲染技术进阶之旅】让我们一起来了解一下什么是glTF?为什么glTF是3D世界的JPEG?
    Java整合七牛云对象存储Kodo
    空域图像增强-图像灰度变换
    后台获取不到请求头中token信息的解决方法
    Unity 渲染YUV数据 ---- 以Unity渲染Android Camera数据为例子
    微信小程序新版隐私协议弹窗实现最新版
  • 原文地址:https://blog.csdn.net/qq_37279783/article/details/127991927