zoukankan      html  css  js  c++  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还提供了自动配置。令人兴奋的是,自动配置还是一种远比显式配置更为迷人的配置方式。至于自动配置有多迷人,我们将在下一章进行介绍。欢迎大家继续阅读,谢谢大家!

    返回目录    下载代码

  • 相关阅读:
    Luogu P3390 【模板】矩阵快速幂&&P1939 【模板】矩阵加速(数列)
    矩阵乘法&&矩阵快速幂&&最基本的矩阵模型——斐波那契数列
    EZ 2018 05 13 NOIP2018 模拟赛(十三)
    Luogu P2341 [HAOI2006]受欢迎的牛
    Deep learning_CNN_Review:A Survey of the Recent Architectures of Deep Convolutional Neural Networks——2019
    常用刷题网址——提高代码能力
    Computer Vision_33_SIFT:Fast Adaptive Bilateral Filtering——2018
    Computer Vision_33_SIFT:An Improved RANSAC based on the Scale Variation Homogeneity——2016
    Computer Vision_33_SIFT:LIFT: Learned Invariant Feature Transform——2016
    Computer Vision_33_SIFT:ORB_An efficient alternative to SIFT or SURF——2012
  • 原文地址:https://www.cnblogs.com/evanlin/p/15455749.html
Copyright © 2011-2022 走看看