上面梳理了通过注解来隐式的完成了组件的扫描和自动装配, 下面来学习下如何通过显式的配置的装配 bean
二, 通过 Java 类装配 bean
在前面定义了 HelloWorldConfig 类, 并使用 @ComponentScan 和 @Configuration 注解 mailto:并使用@ComponentScan和@Configuration注解 ,@Configuration 注解表明了这个类是一个 java 配置类, 该类用在获取 Spring 应用上下文时, 告诉 Spring 创建 bean 的细节, 通过 @ComponentScan mailto:通过@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 mailto:我们通过@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 注解 mailto:我们只添加了@Configuration注解 , 之前的 @ComponentScan 注解移除了 mailto:之前的@ComponentScan注解移除了 , 没有配置启动 Spring 的组件扫描, 另外接口的实现类也没有添加 @Component 注解 mailto:另外接口的实现类也没有添加@Component注解 , 我们通过 @Bean 注解来告诉 Spring 哪些对象需要被注册为 Spring 应用上下文中的 bean mailto:我们通过@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 注解 mailto:通过@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 一样 mailto:同之前@Component一样 , 添加 @Bean 注解的方法返回的 bean 也会被默认分配一个 ID mailto:添加@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 需要的依赖 mailto:我们在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.
来源: http://www.bubuko.com/infodetail-2581271.html