Java教程

基于Java的显式配置

本文主要是介绍基于Java的显式配置,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

经过前文的学习,我们知道基于XML的显式配置就是采用XML显式配置Spring容器。自然而然的,基于Java的显式配置则是采用Java这种编程语言显式配置Spring容器。至于能用Java怎么配置,让我们趁热打铁,紧接前文,看看同样的项目能用基于Java的显式配置怎么实现,进而学习基于Java的显式配置的基础知识。为此,请打开前文实现的music-player项目,新建AppConfig类,修改如下:

1 package com.dream;
2 
3 import org.springframework.context.annotation.*;
4 
5 @Configuration
6 public class AppConfig {
7 }

乍眼一瞧,大家肯定得犯嘀咕:“咋写了个啥也没有的类咧!”可是当我们静下心来仔细瞧上一瞧,就会惊人地发现这个荒芜的类上有个神秘的@Configuration注解。

这是怎么回事呢?

原来,@Configuration是Spring提供的一个注解。这个注解可以把某个类标为配置类,使之具有向Spring容器提供配置信息的能力。由此可见,AppConfig既不是一个啥也没有的类,也不是一个普通的类,而是一个具有@Configuration注解的配置类。只因我们尚未往类里添加配置信息,所以空落落的。既然这样,就让我们敲些代码充实一下该类。如下所示:

 1 package com.dream;
 2 
 3 import org.springframework.context.annotation.*;
 4 
 5 @Configuration
 6 public class AppConfig {
 7     @Bean(name = "music")
 8     public Music produceMusic() {
 9         var music = new Music();
10         music.setMusicName("执着");
11         return music;
12     }
13 
14     @Bean(name = "player")
15     public Player producePlayer() {
16         var music = this.produceMusic();
17         var player = new Player();
18         player.setMusic(music);
19         return player;
20     }
21 }

代码非常简单,就实现了两个方法:一个方法能够创建Music对象;一个方法能够创建Player对象。需要特别留意的是,这两个方法无一例外,都带有一个神秘的@Bean注解。

这是怎么回事呢?

原来,@Bean也是Spring提供的一个注解。这个注解可以把某个方法标为配置方法,使之能被Spring应用上下文发现之后进行调用,从而创建Bean。@Bean注解有个常用的name属性,用于指定Bean的id。其值默认是与之相关的方法的方法名。

于是我们知道了,Spring应用上下文加载AppConfig配置类之后,发现produceMusic方法带有@Bean(name="music")注解。于是调用produceMusic方法创建一个类型为Music,id为music的Bean。发现producePlayer方法带有@Bean(name="player")注解,于是调用producePlayer方法创建一个类型为Player,id为player的Bean。至于Spring应用上下文怎样加载配置类,请看以下代码:

 1 package com.dream;
 2 
 3 import org.springframework.context.annotation.*;
 4 
 5 public class Main {
 6     public static void main(String[] args) {
 7         try (var context = new AnnotationConfigApplicationContext(AppConfig.class)) {
 8             var player = context.getBean("player", Player.class);
 9             player.play();
10             player.pause();
11         }
12     }
13 }

这里用到AnnotationConfigApplicationContext。AnnotationConfigApplicationContext是Spring实现的另外一种Spring应用上下文,能够加载配置类,调用配置方法创建Bean。其构造函数签名如下:

public AnnotationConfigApplicationContext(Class<?>... componentClasses)

这个构建函数接受一个Class<?>... 类型的参数,用于告诉Spring应用上下文能从哪些配置类里加载配置信息。于是,我们创建AnnotationConfigApplicationContext对象的时候传入AppConfig.class,告诉AnnotationConfigApplicationContext加载AppConfig配置类,调用AppConfig配置类里的配置方法创建Bean。

于是,Spring应用上下文加载配置类之后顺利完成了Bean的创建。这些Bean存在Spring应用上下文中,由Spring应用上下文管理着。我们只需调用getBean方法获取id为player,类型为Player的Bean。随后调用Bean的play方法播放音乐,pause方法暂停音乐即可实现音乐播放器。现在运行一下程序,输出如下:

程序是顺利跑起来了,同时我们也开始困惑了。前文曾经提及,Spring应用上下文创建的Bean默认是单例的。可是,Spring应用上下文调用配置方法创建Bean时,不是调用一次方法就创建一个对象,调用两次方法就创建两个对象吗?如此,Spring容器创建的Bean怎么可能还是单例的呢?

为了解开这个迷题,让我们修改一下AppConfig类,添加producePlayer_2方法如下:

 1 package com.dream;
 2 
 3 import org.springframework.context.annotation.*;
 4 
 5 @Configuration
 6 public class AppConfig {
 7     @Bean(name = "music")
 8     public Music produceMusic() {
 9         var music = new Music();
10         music.setMusicName("执着");
11         return music;
12     }
13 
14     @Bean(name = "player")
15     public Player producePlayer() {
16         var music = this.produceMusic();
17         var player = new Player();
18         player.setMusic(music);
19         return player;
20     }
21 
22     @Bean(name="player_2")
23     public Player producePlayer_2() {
24         Music music = this.produceMusic();
25         Player player = new Player();
26         player.setMusic(music);
27         return player;
28     }
29 }

现在,producePlayer和producePlayer_2方法都能创建player对象,并且创建player对象时都会调用produceMusic方法创建music对象进行音乐的注入。于是问题来了,producePlayer方法调用produceMusic方法创建的music对象和producePlayer_2方法调用produceMusic方法创建的music对象是同一个吗?

当然是的。实际上,Spring应用上下文瞧见@Configuration注解之后并不会直接加载AppConfig配置类,而是基于AppConfig配置类生成一个代理类;之后又把带有@Bean注解的配置方法生成代理方法。因此,每次调用produceMusic方法创建music对象的时候,并不是直接调用produceMusic方法创建music对象,而是调用Spring应用上下文生成的代理类里的代理方法进行创建。代理方法创建Bean之前会先判断一下即将创建的对象Spring应用上下文里是不是已经有了。如果已经有了,则直接返回Spring应用上下文里的对象,不再创建。如果Spring应用上下文里还没有这个对象,则调用配置方法进行创建。由是我们的困惑解开了,Spring应用上下文加载配置类之后创建的Bean默认还是单例的。

那么,producePlayer方法创建的player对象和producePlayer_2方法创建的player对象也是同一个对象吗?

当然不是。为什么呢?因为这里定义了两个方法,不同的方法运行不同的代码创建的Bean当然是不同的。至于代理,则是Java这门编程语言的一个高级特性,超出本书的讨论范围。如果大家对此不太了解又想深入学习的话,建议大家阅读一下关于代理的Java书籍或文章。这里不作介绍。

至此,基于Java的显式配置的基础知识介绍完了。前文曾经提及,除了显式配置,Spring还提供了自动配置。令人兴奋的是,自动配置还是一种远比显式配置更为迷人的配置方式。至于自动配置有多迷人,我们将在下一章进行介绍。欢迎大家继续阅读,谢谢大家!

返回目录

这篇关于基于Java的显式配置的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!