美文网首页
SpringBean 配置方式

SpringBean 配置方式

作者: 超_621b | 来源:发表于2019-07-07 15:36 被阅读0次

    上篇文章介绍的IOC的理念与优点,这一章我们介绍如何将自己开发的Bean装配到Spring IOC容器中。

    Spring 配置3种方式

    • 在XML文件中显式配置

    • 在Java 接口实现配置

    • 隐式Bean 的发现机制和自动装配原则

    Spring 如何选择配置?

    这方面,并没有唯一的正确答案。你所做出的选择必须要适合你和你的项目。而且,谁说我们只能选择其中的一种方案呢?Spring的配置风格是可以互相搭配的,所以你可以选择使用XML装配一些bean,使用Spring基于Java的配置(JavaConfig)来装配另一些bean,而将剩余的 bean让Spring去自动发现。 

    装配方式的优先级

    在现实的工作中,这 3 种方式都会被用到,并且在学习和工作之中常常混合使用,所以这里给出一些关于这 3 种优先级的建议:

    隐式Bean的发现机制和自动装配原则

    首选自动装配机制,显示配置越少越好。

    优点:减少程序开发者的决定权,简单又不失灵活。

    Java 的接口和类中实现配置

    在没有办法使用自动装配原则情况,我们考虑此类方法。

    优点:减少xml配置文件,也更为容易。

    xml文件配置

    最后选择xml文件配置。

    优点:简单易懂

    典型场景:

    当使用三方类时,并不是我们开发,也无法修改代码,这个时候通过XML方式配置使用。

    自动化装配Bean


    从两个角度来实现自动化装配

    • 组件扫描(component scaning)   

    • 自动装配(autowing)

    组件扫描

    Spring 会自动发现应用上下文所创建的Bean。

    自动装配(autowing)

    Spring 自动满足Bean之间的依赖。组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。

     代码实现

    在这个MP3和流式媒体音乐的时代,磁带显得陈旧,随着以物理载体方式越来越少。尽管如此,磁带为我们阐述DI如何运行提供一个很好的样例。如果不将磁带放入磁带播放器中,那么磁带其实没有太大用处,所有可以说磁带依赖于磁带播放器。

    磁带接口 

    public interface Tape {

        void play();
    }

    熊猫播放器实现类

    @Component
    public class PandaPlayer implements Tape {

    private String singer = "周杰伦";

    private String songName = "双截棍";

    public void play() {
        System.out.println("开始播放:"+singer+"的"+songName);
    }

    }

    配置类

    @Configuration
    @ComponentScan
    public class TapeConfig {

    }

    测试类

    @RunWith(SpringJUnit4ClassRunner.class)
     // 自动装配的配置
     @ContextConfiguration(classes = TapeConfig.class)
     public class PlayerTest {

    // 1、通过扫描包的方式实现装配bean,使用@Component注解或@Named 注解进行配置
    @Autowired 
    private Tape tape;

    @Test 
    public void player(){ 
        assertNotNull(tape); tape.play();
    }
    }

    测试结果

    开始播放:周杰伦的双截棍

    如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为TapeConfig类位于com.automatic.service包中,因此Spring 将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现Tape,并且会在Spring中自动为 其创建一个bean。


    自动装配

    简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的@Autowired注解。

    使用@Autowired是放到构造方法中,@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上或者普通方法中。 

    Player播放接口

    public interface Player {

    void play();
    }

    索尼播放器实现类

    @Component
    public class SonyPlayer implements Player {

    private Tape tape;

    @Autowired
    public SonyPlayer (Tape tape) {
        this.tape = tape;
    }

    public void play() {
        System.out.println("----start----");
        tape.play();
        System.out.println("----end----");
    }

    }

    测试方法

    @Autowired
     private Player player;

     @Test public void player(){
          assertNotNull(player); 
          player.play(); 
    }  

    测试结果

     ----start----
     开始播放:周杰伦的双截棍
     ----end----

     


    通过Java代码装配Bean

    尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring。比如说,你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。

    下面的例子是参考《Sring实战第四版》,觉得比较容易理解:

    CD播放类

    public class CDPlayer {

    private CompactDisc cd;

    public void play() {
        cd.play();
    }
    // 省略setget方法
    }

    播放接口

    public interface CompactDisc {

    void play();
    }

    歌曲

     public class HardDaysNight  implements CompactDisc {

    private String title = "Hard. DaysNight's Lonely Hearts Club Band";
    private String artist = "The Hard";

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }


    }

    public class Revolver implements CompactDisc {

    private String title = "Revolver. Revolver's Lonely Hearts Club Band";
    private String artist = "The Revolver";

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
    }

    public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
    }

    public class WhiteAlbum  implements CompactDisc {

    private String title = "White. Album's Lonely Hearts Club Band";
    private String artist = "The White";

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
    }

    重点在这里Config配置类

     /*
     * Spring 的组件扫描默认是不启用的,需要显式配置启用组件扫描去寻找被 
     @Component 注解修饰的组件类,并为其创建 bean 实例。
     * */
     /*
      * 标记类 CDPlayerConfig 是 Spring 的配置类,通过 java 代码定义 
      Spring 的装配规则。
      * 该类应该包含在Spring应用上下文中如何创建bean的细节。
      * */
    @Configuration
    public class CDPlayerConfig {


        /*
        * 要在 JavaConfig 中声明 bean ,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加 @Bean 注解。
        *
        * @Bean 注解会告诉 Spring 这个方法将会返回一个对象,该对象要注册为 Spring 应用上下文中的 bean 。
        * 方法体中包含了最终产生 bean 实例的逻辑。
        *
        * 默认,bean 的 ID 和方法名是一样的,如下 bean 的 ID 为 sgtPeppers 。
        * 如果想手动为 bean 指定一个 ID ,可以使用 @Bean 的 name 属性,如:
        *   @Bean(name="lonelyHeartsClubBand")
        * */
        @Bean
        public CompactDisc sgtPeppers() {
            return new SgtPeppers();
        }

        // 这里是使用 Java 创建 bean,因此我们可以发挥 Java 提供的所有功能,只要最终生成一个 CompactDisc 实例即可。例如:
        @Bean
        public CompactDisc randomBeatlesCD() {
            CompactDisc cd;
            int choice = (int) Math.floor(Math.random() * 4);
            switch (choice) {
                case 1:
                    cd = new SgtPeppers();
                    break;
                case 2:
                    cd = new WhiteAlbum();
                    break;
                case 3:
                    cd = new HardDaysNight();
                    break;
                default:
                    cd = new Revolver();
                    break;
            }
            return cd;
        }

        /*
        * Spring 装配方式一:
        * 在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。
        * */
        @Bean
        public CDPlayer cdPlayer() {
      //            return new CDPlayer(sgtPeppers());
            return new CDPlayer(new WhiteAlbum());
        }

        /*
        * Spring 装配方式二:
        * 当 Spring 调用 cdPlayer() 创建 CDPlayer bean 的时候,它会自动装配一个 CompactDisc 到配置方法之中。
        * 然后,方法体就可以按照合适的方式来使用它。
        *
        * 通过这种方式引用其他的 bean 通常是最佳的选择,因为它不会要求将 CompactDisc 声明到同一个配置类之中。
        * 在这里甚至没有要求 CompactDisc 必须要在 JavaConfig 中声明,
        * 实际上它可以通过组件扫描功能自动发现或者通过 XML 来进行配置。
        * 你可以将配置分散到多个配置类、XML 文件以及自动扫描和装配 bean 之中,
        * 只要功能完整健全即可。不管 CompactDisc 是采用什么方式创建出来的,
        * Spring 都会将其传入到配置方法中,并用来创建 CDPlayer bean 。
        *
        * 当配置类中有多个同类型的 bean 时,此时可以使用 @Qualifier 注解来指定参数注入的是哪一个具体的 bean 。
        * */
        @Bean
        public CDPlayer cdPlayer(@Qualifier("randomBeatlesCD") CompactDisc compactDisc) {
            return new CDPlayer(compactDisc);
        }

        /*
        * 我们也可以采用属性赋值的方式来注入依赖值,这里所存在的可能性仅仅受到Java语言的限制。
        * */
      //        @Bean
    //        public CDPlayer cdPlayer(@Qualifier("sgtPeppers") 
      CompactDisc compactDisc) {
      //            CDPlayer cdPlayer = new CDPlayer();
     //            cdPlayer.setCd(compactDisc);
     //            return cdPlayer;
      //        }
       }

    测试方法

     @Autowired
     private CDPlayer cdPlayer;

     @Test
     public void cdShouldNotBeNull() {
        assertNotNull(cdPlayer);
        cdPlayer.play();
     }

    测试结果

     Playing Revolver. Revolver's Lonely Hearts Club Band by The Revolver

    每次运行的结果是不同,因为randomBeatlesCD()方法。


    XML文件装配Bean

    使用xml文件进行配置,配置比较简单易懂。

    public class ComplexAssembly {

    private Long id;

    private List<String> list;

    private Map<String, String> map;

    private Properties properties;

    private Set<String> set;

    private String[] array;

    // 省略setget方法
    }

    这个 Bean 没有任何的实际意义,只是为了介绍如何装配这些常用的集合类:

    xml文件

    <bean id="complexAssembly" class="com.xml.service.ComplexAssembly">
    <!-- 装配Long类型的id -->
    <property name="id" value="1"/>

    <!-- 装配List类型的list -->
    <property name="list">
        <list>
            <value>value-list-1</value>
            <value>value-list-2</value>
            <value>value-list-3</value>
        </list>
    </property>

    <!-- 装配Map类型的map -->
    <property name="map">
        <map>
            <entry key="key1" value="value-key-1"/>
            <entry key="key2" value="value-key-2"/>
            <entry key="key3" value="value-key-2"/>
        </map>
    </property>

    <!-- 装配Properties类型的properties -->
    <property name="properties">
        <props>
            <prop key="prop1">value-prop-1</prop>
            <prop key="prop2">value-prop-2</prop>
            <prop key="prop3">value-prop-3</prop>
        </props>
    </property>

    <!-- 装配Set类型的set -->
    <property name="set">
        <set>
            <value>value-set-1</value>
            <value>value-set-2</value>
            <value>value-set-3</value>
        </set>
    </property>

    <!-- 装配String[]类型的array -->
    <property name="array">
        <array>
            <value>value-array-1</value>
            <value>value-array-2</value>
            <value>value-array-3</value>
        </array>
    </property>
    </bean>

    测试方法

         @Test
     public void getXmlComplexAssembly() {
         ApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[]{"applicationContext.xml"}
                    );

         ComplexAssembly complexAssembly = (ComplexAssembly) context.getBean("complexAssembly");
         System.out.println(complexAssembly.getId());
         System.out.println(complexAssembly.getMap().get("key1"));

     }

    测试结果

      1
      value-key-1

    总结:
          List 属性为对应的 <list> 元素进行装配,然后通过多个 <value> 元素设值
          Map 属性为对应的 <map> 元素进行装配,然后通过多个 <entry> 元素设值, 只是 entry 包含一个键值对(key-value)的设置
          Properties 属性为对应的 <properties> 元素进行装配,通过多个 <property> 元素设值,只是 properties 元素有一个必填属性 key ,然后可以设置值
          Set 属性为对应的 <set> 元素进行装配,然后通过多个 <value> 元素设值
     对于数组而言,可以使用 <array> 设置值,然后通过多个 <value> 元素设值。



    命名空间装配


    除了上述的配置之外, Spring 还提供了对应的命名空间的定义,只是在使用命名空间的时候要先引入对应的命名空间和 XML 模式(XSD)文件。

    • p-命名空间

    • c-命名空间

    • util-命名空间


    p标签

    使用p命名空间需要添加

    <!-- 使用p标签前 -->                        
     <bean id="complexAssembly" 
     class="com.xml.service.ComplexAssembly">
        <property name="id" value="1"/>
     </bean>
    <!-- 使用p标签后 -->
    <bean name="complexassembly2" class="com.xml.service.ComplexAssembly"  p:id="2" />

    c标签

    通过这个类举例子

     package pojo;
     public class Student { 
       int id; 
       String name;
      public Student(int id, String name) 
      { 
       this.id = id; this.name = name; 

      } 
       // setter and getter 
    }

     在 c-命名空间和模式声明之后,我们就可以使用它来声明构造器参数了:

     <!-- 引入 c-命名空间之前 -->
     <bean name="student1" class="pojo.Student">
       <constructor-arg name="id" value="1" />
        <constructor-arg name="name" value="学生1"/>
    </bean>

     <!-- 引入 c-命名空间之后 -->
    <bean name="student2" class="pojo.Student"
           c:id="2" c:name="学生2"/>



    util标签

    <!-- 引入util-命名空间之前 -->
    <property name="list">
    <list>
        <ref bean="bean1"/>
        <ref bean="bean2"/>
    </list>
    </property>

    <!-- 引入util-命名空间之后 -->
    <util:list id="list">
       <ref bean="bean1"/>
       <ref bean="bean2"/>
     </util:list>

    <util:list> 只是 util-命名空间中的多个元素之一,下表提供了 util-命名空间提供的所有元素:

    Spring 作用域

    总  结


    在Spring中装配bean的三种主要方式:自动化配置、基于Java的显式配置以及基于XML的显式配置。不管你采用什么方式,这些技术都描述了Spring应用中的组件以及这些组件之间的关系。 

    本文中源码:

    https://github.com/xiaonongOne/spring-bean

    下面是微信公众号二维码,欢迎来骚扰。


    相关文章

      网友评论

          本文标题:SpringBean 配置方式

          本文链接:https://www.haomeiwen.com/subject/eqnbhctx.html