美文网首页
Spring装配bean的三种方式

Spring装配bean的三种方式

作者: 我是予不真 | 来源:发表于2018-07-12 01:10 被阅读0次

    所有内容参考《spring 实战》,作为学习笔记使用。

    spring装配bean主要有三种方式,(1) 自动装配 (2) java代码显式装配 (3) xml显式装配

    1. 自动化装配

    spring的自动化装配主要通过两个机制来实现,组件扫描、自动装配
    组件扫描:Spring会自动发信应用上下文中所创建的bean
    自动装配:Spring自动满足bean之间的依赖
    下面通过代码来讲解,主要涉及4个java文件,两个接口,两个实现类。分别是
    SgtPeppers实现CompactDisc,CDPlayer实现MediaPlayer

    package soundsystem;
    public interface CompactDisc {
        void play();
    }
    
    package soundsystem;
    
    import org.springframework.stereotype.Component;
    
    @Component("sgtPeppers")  //设置生成的bean的ID
    //Component表示该类会作为组件类,并且告诉spring为该类创建bean,在spring中,组件扫描默认不启用,需要显示配置一下,配置完之后,spring会去扫描带有@Component的组件,并生成bean
    //显示配置在CDPlayerConfig.java中配置了
    public class SgtPeppers implements CompactDisc {
    
        private String title = "Sgt. Pepper's Lonely Hearts";
        private String author = "The Beatles";
    
        @Override
        public void play() {
            System.out.println("playing " + title + " by " + author);
        }
        public static void main(String[] args){
    
        }
    }
    
    package soundsystem;
    
    public interface MediaPlayer {
        void play();
    }
    
    package soundsystem;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CDPlayer implements MediaPlayer {
        private CompactDisc cd;
        /*
        * @Autowired实现依赖的注入。
        * */
        @Autowired  //spring会努力寻找一个可以设置为CompactDisc类型的bean传递给构造函数,如果spring找不到这样的bean,会抛异常,同时,如果找到多个这样的bean
        //也会抛异常
        public CDPlayer(CompactDisc cd) {
            this.cd = cd;
        }
    
        public CDPlayer(){
    
        }
        public void setCd(CompactDisc cd){
            this.cd = cd;
        }
        public void play() {
            cd.play();
        }
    }
    

    在SgtPeppers和CDPlayer类上都加了@Component注解,这个注解表示这个类会作为组件类,并告知Spring要为这个类创建Bean。所以,没必要显示的配置SgtPeppers的bean,Spring知道如何处理。
    其实,写完了上边的代码还差一步,就是要告诉Spring要扫描哪个位置的组件。这里需要写一个配置类,这个类可以启用组件扫描。

    package soundsystem;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    /*
    * 这个类定义了spring的装配规则。
    * 在没有其他配置的时候,@ComponentScan注解默认扫描与配置类相同的包,即soundsystem包。所以,这个包下的SgtPeppers就会被扫到了。
    * */
    @Configuration
    @ComponentScan
    /*
    * 默认情况下,只扫描配置类所在的包,如果我想要扫描更多的包应该怎么做呢?
    * @ComponentScan(basePackages = {"soundsystem","video"})    ,参数设置成想要扫的包的数组就可以了
    * 为了安全考虑(因为写字符串毕竟会出错),可以使用:
    * @ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class})   ,参数设置成class的数组,这样更安全。
    * */
    public class CDPlayerConfig {
    
    }
    

    @Configuration注解声明这是一个配置类,它负责定义Spring的装配规则。从代码中可以看到,我们并没有显示的声明任何bean。
    @ComponentScan默认会扫描与配置类相同的包。这个配置类与我们之前写的代码在同一个包下,所以,Spring会扫描配置类所在的包和这个包下的所有子包,查找带有@Component的类。这样就能扫到CompactDisc和CDPlayer了。
    在上面的代码中,还有一个@Autowired的注解,它表明当Spring创建CDPlayer bean的时候,会对它进行实例化并会传入一个Compactdisc类型的bean。这样,实例之间的依赖关系就满足了。同时@Autowired注解还可以用在任何方法上,例如Setter方法上:

    @Autowired
    public void setCompactDisc(CompactDisc cd) {
      this.cd = cd;
    }
    

    在Spring初始化bean后,会尽可能的去满足bean的依赖。如果没有匹配的bean,Spring会抛异常。
    下面是针对上面代码的Test

    package soundsystem;
    
    import static org.junit.Assert.*;
    
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
    /*
    * 这个类就是用来测试CompactDisc的bean有没有被生成
    * */
    @RunWith(SpringJUnit4ClassRunner.class)     //生成spring应用上下文
    @ContextConfiguration(classes = CDPlayerConfig.class)   //告诉spring需要在CDPlayerConfig中加载配置
    public class CDPlayerTest {
    
        @Rule
        public final StandardOutputStreamLog log = new StandardOutputStreamLog();
    
        @Autowired
        private MediaPlayer player;
    
        @Autowired
        private CompactDisc cd;         //SgtPeppers cd ;  也成立
    
        //验证自动创建bean功能
        @Test
        public void test(){
            assertNotNull(cd);          //如果不为null表示生成了。
        }
    
        //验证自动装配功能
        @Test
        public void play() {
            player.play();
            assertEquals("playing Sgt. Pepper's Lonely Hea
    rts by The Beatles\n",log.getLog());
        }
    }
    

    2. 通过java代码装配bean

    在看Spring实战这本书的时候,作者一直在强调在需要进行显示配置的时候,通过JavaConfig是更好的方式,因为,我们要更关注这种方式。
    为什么要进行显示配置呢?通过组件扫描和自动装配不是更好吗?因为我们在做工程的时候,经常会遇到第3方的代码,这个时候我们总不能到别人的代码上去加@component和@Autowired吧。
    其实,通过java代码显示配置也很简单,在上面我们已经使用过了。

    package soundsystem;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    /*
    * 这个类定义了spring的装配规则。
    * 在没有其他配置的时候,@ComponentScan注解默认扫描与配置类相同的包,即soundsystem包。所以,这个包下的SgtPeppers就会被扫到了。
    * */
    @Configuration
    //@ComponentScan
    /*
    * 默认情况下,只扫描配置类所在的包,如果我想要扫描更多的包应该怎么做呢?
    * @ComponentScan(basePackages = {"soundsystem","video"})    ,参数设置成想要扫的包的数组就可以了
    * 为了安全考虑(因为写字符串毕竟会出错),可以使用:
    * @ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class})   ,参数设置成class的数组,这样更安全。
    * */
    public class CDPlayerConfig {
    
        @Bean
        public CompactDisc sgtPeppers(){
            return new SgtPeppers();
        }
    
        @Bean
        public CDPlayer cdPlayer(CompactDisc cd ){
            return new CDPlayer(cd);
    
        }
    }
    

    我们把@ComponentScan注释掉,这样Spring就不会自动创建bean了。然后我们在JavaConfig中声明@Bean,它会告诉Spring这个方法会返回一个对象,该对象要注册为Spring应用上下文的Bean。对于sgtPeppers()就是这样的,它不涉及别的依赖。对于cdPlayer(),它有对CompactDisc的依赖,需要请求一个CompactDisc作为参数,当Spring调用该方法创建CDPlayer bean的时候,会自动装配一个CompactDisc到配置方法中。同时,对于cdPlayer()下面两种方法也是可行的

     @Bean
        public CDPlayer cdPlayer(CompactDisc cd ){
            CDPlayer player = new CDPlayer();
            player.setCd(cd);
            return player;
    
        }
    @Bean
    public CDPlayer cdPlayer() {
      return new CDPlayer(sgtPeppers());
    }
    

    3.通过xml装配Bean

    用xml装配Bean与java代码装配Bean思路是一样的。只不过前者是通过xml配置,后者是通过JavaConfig配置。所以,要先有一个xml的配置文件。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <!-- configuration details go here-->
    </beans>
    

    上面是配置文件的基本格式,以<beans>为根,在顶部需要声明多个xml模式文件。然后<bean>的配置在里边写
    (1) 声明简单<bean>

    <bean class="soundsystem.SgtPeppers" />
    <bean id="compactDisc" class="soundsystem.SgtPeppers" /> 
    <!-- 上面两条语句都相当于JavaConfig中的@Bean注解,用第一条配置时,会给
    生成的Bean分配一个默认的Id,在这里就是soundsystem.Sgtpeppers#0,如果
    再有一个相同的bean,会分配#1-->
    <!--第2条语句是自定义bean ID,为compactDisc,除了id不一样,两者的效果是一样的-->
    

    当Spring发现这个<bean>时,会调用默认的构造器来创建bean。
    (2)借助构造器注入初始化bean
    实现注入有两种方式:

    • <constructor-arg>元素
    <bean id = "cdPlayer" class = "soundsystem.CDPlayer">
      <constructor-arg ref = "compactDisc" />
    <!-- ref 后边加的就是bean的id,这是当参数是引用的时候-->
    </bean>
    <!--当参数是字符串的时候***********-->
    <bean id = "cdPlayer" class = "soundsystem.BlankDisc"/>
      <constructor-arg value = "Sgt. Pepper's Lonely Hearts Club Band"/>
      <constructor-arg value = "字符串参数">
    <!-- value 后边加的就是具体的字符串-->
    </bean>
    

    当构造器参数中某个参数时列表时,只能用<constructor-arg>

    <bean id = "cdPlayer" class = "soundsystem.BlankDisc"/>
      <constructor-arg value = "Sgt. Pepper's Lonely Hearts Club Band"/>
      <constructor-arg value = "字符串参数">
      <constructor-arg>
        <list>
            <value>value1</value>
            <value>value2</value>
            <value>value3</value>
        </list>
      </constructor-arg>
    <!-- value 后边加的就是具体的字符串-->
    </bean>
    <!--如果列表中存的是对象的引用的时候-->
    <list>
      <ref bean="bean1" />
      <ref bean="bean2" />
      <ref bean="bean3" />
    </list>
    
    • Spring3.0引入的c-命名空间
      使用c-命名空间,要在xml的顶部声明其模式。
      xmlns:c="http://www.springframework.org/schema/c
    <bean id="cdPlayer" class="soundsystem.CDPlayer"
          c:cd-ref="compactDisc" />
    

    c:是c-命名空间前缀,cd是构造器参数名,-ref注入bean的引用

    c:_0-ref="compactDisc" 表示构造器的第一个参数
    c:_1-ref="****"  构造器的第二锅参数
    当构造器只有一个参数时,可以是
    c:_-ref="param_name"
    

    相关文章

      网友评论

          本文标题:Spring装配bean的三种方式

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