• 通过Java代码装配Bean


    上面梳理了通过注解来隐式的完成了组件的扫描和自动装配,下面来学习下如何通过显式的配置的装配bean

    二、通过Java类装配bean

    在前面定义了HelloWorldConfig类,并使用@ComponentScan和@Configuration注解,@Configuration注解表明了这个类是一个java配置类,该类用在获取Spring应用上下文时,告诉Spring创建bean的细节,通过@ComponentScan,我们启用了Spring的自动组件扫描,现在就让我们来看如果通过java类来显式的配置bean,下面我们通过一个音乐播放器的案例来实践一下。

    我们播放音乐,首先需要一个播放器,然后需要音乐资源,首先我们定义一个播放器接口和音乐资源接口

    package com.seven.springTest.service;
    
    // 播放器
    public interface MediaPlayer {  
        void play();
    }
    package com.seven.springTest.service;
    
    // 音乐资源
    public interface MusicSource {
    
        void play();
    }

    本次播放音乐我们是光驱来播放cd音乐,下面我们来实现上面的接口,

    package com.seven.springTest.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.seven.springTest.service.MusicSource;
    import com.seven.springTest.service.MediaPlayer;
    
    //定义光驱播放器
    public class CDPlayer implements MediaPlayer {
        
        @Autowired
        // 定义一个音乐资源,这里通过@Autowired来声明需要注入MusicSource的依赖
        private MusicSource cd ;
    
        @Override
        public void play() {
            //播放音乐
            cd.play();
        }
    }
    

    实现音乐资源为光盘

    package com.seven.springTest.service.impl;
    import com.seven.springTest.service.MusicSource;
    
    public class CDSource implements MusicSource {
    
        private String title = "七里香";
        private String artist = "周杰伦";
    
        @Override
        public void play() {
            System.out.println("Playing " + title + " by " + artist);
        }
    }

    到目前为止我们已经完成播放器、音乐资源的接口定义和具体的实现,那么我们如果告诉Spring应该创建哪么bean,并为它们注入什么依赖呢?在第一部分,我们通过@Component注解来隐式的告诉Spring,现在我们通过java类来配置bean组件。

    @Bean

    @Bean注解告诉Spring函数将返回一个对象,该对象需要注册为Spring应用上下文中的bean,该方法中包含了产生bean实例的逻辑

    package com.seven.springTest.Configuration;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    import com.seven.springTest.service.MusicSource;
    import com.seven.springTest.service.MediaPlayer;
    import com.seven.springTest.service.impl.CDPlayer;
    import com.seven.springTest.service.impl.CDSource;
    
    @Configuration
    public class MediePlayerConfig {
        
        @Bean   //该方法返回的MusicSource对象需要注册为Spring应用上下文中的bean
        public MusicSource cdsource(){
            return new CDSource();
        }
        
        @Bean  //该方法返回的MediaPlayer对象需要注册为Spring应用上下文中的bean
        public MediaPlayer cdplayer(){
            return new CDPlayer();
        }
        
    }

    MediePlayerConfig类中,我们只添加了@Configuration注解之前的@ComponentScan注解移除了,没有配置启动Spring的组件扫描,另外接口的实现类也没有添加@Component注解我们通过@Bean注解来告诉Spring哪些对象需要被注册为Spring应用上下文中的bean。cdsource()方法返回了一个MusicSource类型的实例对象CDSource,该对象被注册到Spring应用上下文,同样的cdplayer()方法返回了一个MediaPlayer类型的实例CDPlayer注册到Spring应用上下文中。下面我们来测试下

    package com.seven.springTest.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import com.seven.springTest.Configuration.MediePlayerConfig;
    import com.seven.springTest.Configuration.HelloWorldConfig;
    import com.seven.springTest.service.MediaPlayer;
    
    public class MediaPlayerTest {
        public static void main(String[] args) {
            //加载java配置类获取Spring应用上下文
            ApplicationContext ac = new AnnotationConfigApplicationContext(MediePlayerConfig.class);
            //获取播放器
            MediaPlayer player= ac.getBean(MediaPlayer.class);
            //播放
            player.play();
        }
    }

    我们在获取播放器bean的时候,其实获取到的就是MediePlayerConfig类中cdplayer()返回的对象CDPlayer,在CDPlayer中我们依赖MusicSource,通过@Autowired注解,Spring自动为该bean注入了对MusicSource的依赖,所以在测试代码中我们只是获取了MediaPlayer对象的实例player,至于player有哪些依赖,我们都不知道,都是由Spring容器来给我注入,这里只关心播放器player,这就是Spring给我们带来的便捷,我们不需要用代码去管理对象的依赖关系,对象所有依赖的资源都有Spring容器来为我们注入。

    随着技术的发展,有一天光驱也可以插入U盘播放MP3音乐了,这个时候我们来实现一个MP3的音乐资源

    package com.seven.springTest.service.impl;
    
    import com.seven.springTest.service.MusicSource;
    
    public class MP3Source implements MusicSource {
        
        private String title = "外婆";
        private String artist = "周杰伦";
    
        @Override
        public void play() {
            // TODO Auto-generated method stub
            System.out.println("MP3 Playing " + title + " by " + artist);
        }
    
    }

    在第一部分自动装配中,如果Spring发现了多个bean满足依赖关系,Spring就无法选择了,那么如果我们定义了MP3Source的实现,现在会不会也出现这样的情况呢?通过运行程序,我们发现没有产生任何影响,CDPlayer bean被注入的MusicSource依赖还是CDSource。这是因为我们在MediePlayerConfig中通过cdsource()告知了Spring产生bean的实现逻辑,那我们来修改下cdsource()

    @Bean   //该方法返回的MusicSource对象需要注册为Spring应用上下文中的bean
    public MusicSource cdsource(){
        //返回MP3Source实例
        return new MP3Source();
    }

    我们再运行下测试方法,发现输出内容变成了“==MP3 Playing 外婆 by 周杰伦==”,说明注入的依赖对象实现发生变化了,这是因为 cdsource()内实现的是返回MP3Source的实例。

    同之前@Component一样添加@Bean注解的方法返回的bean也会被默认分配一个ID,默认情况下和方法名相同,如cdsource()方法返回的bean的ID就为“cdsource”,我们也可以指定bean的ID,如下:

    @Bean(name="myCdplayer")      //该方法返回的MediaPlayer对象需要注册为Spring应用上下文中的bean
    public MediaPlayer cdplayer(){
        return new CDPlayer();
    }

    这样在获取bean的时候就可以通过ID来获取

    public class MediaPlayerTest {
        public static void main(String[] args) {
            //加载java配置类获取Spring应用上下文
            ApplicationContext ac = new AnnotationConfigApplicationContext(MediePlayerConfig.class);
            //根据ID获取bean
            MediaPlayer player= (MediaPlayer) ac.getBean("myCdplayer");
            //播放
            player.play();
        }
    }

    上面的案例中CDPlayer bean它依赖了MusicSource的依赖,我们在CDPlayer类中通过@Autowired声明了CDPlayer需要的依赖,这里还是一种通过注解隐式的配置,下面我们来通过java配置类来实现。

    如果是显式的配置,由于MediePlayerConfig中配置的bean都是通过方法返回的,所以需要在返回对象bean的方法里注入依赖:

    @Bean(name="myCdplayer")  //该方法返回的MediaPlayer对象需要注册为Spring应用上下文中的bean
    public MediaPlayer cdplayer(){
        return new CDPlayer(cdsource());   //通过对象的构造函数注入依赖
    }

    或者

    @Bean(name="myCdplayer")  //该方法返回的MediaPlayer对象需要注册为Spring应用上下文中的bean
    public MediaPlayer cdplayer(MusicSource musicSource){
        return new CDPlayer(musicSource);   //通过对象的构造函数注入依赖
    }

    通过上面2中方式配置,Spring都可以对CDPlayer中的MusicSource对象完成依赖注入,下面我们在定义一个bean配置

    @Bean(name="myCdplayer")  //该方法返回的MediaPlayer对象需要注册为Spring应用上下文中的bean
    public MediaPlayer cdplayer(){
        return new CDPlayer(cdsource());   //通过对象的构造函数注入依赖
    }
    
    @Bean(name="otherCdplayer")   //定义另外一个bean对象,
    public MediaPlayer othercdplayer(){
        return new CDPlayer(cdsource());
    }
    

    MediaPlayer接口增加一个获取播放资源的方法

    package com.seven.springTest.service;
    
    public interface MediaPlayer {
    
        /**
        * 获取播放器加载的资源 
        * @return MusicSource
        */
        MusicSource getResource();
    
        /**
         * 播放
         */
        void play();
    }

    解下列,我们修改下Test代码

    package com.seven.springTest.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import com.seven.springTest.Configuration.MediePlayerConfig;
    import com.seven.springTest.Configuration.HelloWorldConfig;
    import com.seven.springTest.service.MediaPlayer;
    
    public class MediaPlayerTest {
        public static void main(String[] args) {
            // 加载java配置类获取Spring应用上下文
            ApplicationContext ac = new AnnotationConfigApplicationContext(MediePlayerConfig.class);
            // 获取播放器
            MediaPlayer player = (MediaPlayer) ac.getBean("myCdplayer");
            MediaPlayer otherplayer = (MediaPlayer) ac.getBean("otherCdplayer");
            if (player.getResource().equals(otherplayer.getResource())) {
                System.out.println("true");
            }
            // 播放
            //player.play();
        }
    }

    运行后,我们发现输出“true”,这说明的什么情况呢,我们在cdplayer()和othercdplayer()方法中在调用CDPlayer(cdsource())构造时,通过cdsource()获取的音乐资源对象是相同的,在默认情况下,Spring中的bean都是单例的,Spring会拦截对cdsource()的调用,并确保返回的是Spring创建的bean,也就是Spring本身在第一次调用cdsource()所创建的bean。

  • 相关阅读:
    vue 交互 跨域获取数据
    计算属性computed缓存 与 methods 的思考
    _this 与 this
    python 占位符 %s Format
    odoo 中字段属性对象Field
    安装CentOS7.7图解
    docker的volumes
    Docker常用命令详解
    Ubuntu修改时区和更新时间
    SqlServer创建时间维度
  • 原文地址:https://www.cnblogs.com/duan2/p/8973457.html
Copyright © 2020-2023  润新知