• Spring-装配Bean


    • 创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。
    • Spring提供三种主要的装配机制:

        1.在XML中进行显式配置;

        2.在java中进行显式配置;

        3.隐式的bean发现机制和自动装配。

        注:这三种机制可以自由搭配,但是建议尽可能地使用自动配置的机制,显式配置越少越好,并且尽量使用java进行配置,即使用注解的方式进行配置。

    • Spring从两个角度实现自动化装配:

        1.组件扫描(component scanning):Spring会自动发现上应用上下文中所创建的bean。

        2.自动装配(autowiring):Spring自动满足bean之间的依赖。

        注:组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将显式配置降低到最少。

    package soundSystem;
    /**
     * 创建CD接口,只有一个cdInfo方法来在控制台显示CD相关信息
     * @author yan
     */
    public interface CompactDisc {
        void cdInfo();
    }
    package soundSystem;
    
    /**
     * CompactDisc接口的实现类,此类中定义了CD的名字和作者以及打印相关信息的方法
     * @author yan
     */
    public class SgtPeppers implements CompactDisc{
    
        private String title="K歌之王";
        private String artist="陈奕迅";
        @Override
        public void cdInfo() {
            System.out.print("This CD's title:"+title+";artist:"+artist);
        }
    
    }
    package soundSystem;
    /**
     * 定义多媒体播放器接口,包含play方法
     * @author yan
     */
    public interface MediaPlayer {
        void play();
    }
     1 package soundSystem;
     2 /**
     3  * 定义多媒体播放器的实现类,并使用有参构造关联了CompactDisc接口,重写了play方法
     4  * @author yan
     5  */
     6 public class CDPlayer implements MediaPlayer{
     7 
     8     private CompactDisc cd;
     9     
    10     public CDPlayer(CompactDisc cd) {
    11         super();
    12         this.cd = cd;
    13     }
    14     public void setCd(CompactDisc cd) {
    15         this.cd = cd;
    16     }
    17     @Override
    18     public void play() {
    19         System.out.print("Info of CD playing :");        
    20         cd.cdInfo();
    21     }
    22 
    23 }

    以上四段代码仅仅列举出了CD和播放器的接口、实现类以及它们之间的依赖关系,比如,CDPlayer的构造方法中传入了CD类型的参数。

    Spring如何实现依赖注入呢?我们以隐式配置为例:

      首先,在同一包下(soundSystem)创建配置类CDPlayerConfig,效果如下

     1 package soundSystem;
     2 
     3 import org.springframework.context.annotation.ComponentScan;
     4 import org.springframework.context.annotation.Configuration;
     5 
     6 @Configuration
     7 @ComponentScan//如果此处不加任何参数,默认扫描与配置类相同的包
     8 //@ComponentScan("soundSystem")//指定单独的包,这个包内主要放置bean配置
     9 //@ComponentScan(basePackages={"soundSystem","video"})//允许指定多个包,但是,这种用字符串表示的包不安全,可以尝试使用class方式
    10 //@ComponentScan(basePackageClasses={CompactDisc.class,SgrPeppers.class})
    11 public class CDPlayerConfig {}

    @Configuration表明这是一个配置类,@ComponentScan表示自动扫描,其不同用法见代码注释。

    此时,只是扫描包,但并无作用,因为没有相关标志表明它是扫描的目标,即所要生成的bean,而Spring之所以存在是因为解耦和,即不用传统方法来new一个新的实例,因此在实现类中使用@Component标明,即可达到效果,代码如下:

    package soundSystem;
    
    import org.springframework.stereotype.Component;
    /**
     * CompactDisc接口的实现类,此类中定义了CD的名字和作者以及打印相关信息的方法
     * @author yan
     */
    @Component("compactd")//用于扫描来生成bean,并且提供了初始化所用的值
    public class SgtPeppers implements CompactDisc{
    
        private String title="K歌之王";
        private String artist="陈奕迅";
        @Override
        public void cdInfo() {
            System.out.print("This CD's title:"+title+";artist:"+artist);
        }
    
    }

    Component不加参数时,默认id为第一个字母小写的类名:sgtPeppers,有参数则id为参数。

    注:这里CompactDisc是独立存在的,不依赖任何其他类的,因此不需要装配,对于MediaPlayer则需要,因为其实现类中明确定义了CompactDisc类型的有参构造方法,所以,仍然需要标明依赖关系,使用@AutoWired可以达到目的,代码如下:

     1 package soundSystem;
     2 
     3 import org.springframework.beans.factory.annotation.Autowired;
     4 import org.springframework.stereotype.Component;
     5 /**
     6  * 定义多媒体播放器的实现类,并使用有参构造关联了CompactDisc接口,重写了play方法
     7  * @author yan
     8  */
     9 @Component
    10 public class CDPlayer implements MediaPlayer{
    11     private CompactDisc cd;
    12     @Autowired
    13     public CDPlayer(CompactDisc cd) {
    14         super();
    15         this.cd = cd;
    16     }
    17     @Override
    18     public void play() {
    19         System.out.print("Info of CD playing :");        
    20         cd.cdInfo();
    21     }
    22 
    23 }

    这表明不但创造了一个叫cDPlayer的bean,而且还在bean中引用(或者说自动装配)了名叫compactd的bean为参数。

    注意:此处使用的自动装配的对象是有参构造方法,而Spring还提供了Setter方法的自动装配方式,用法与此相同。

    package soundSystem;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    /**
     * 定义多媒体播放器的实现类,并使用有参构造关联了CompactDisc接口,重写了play方法
     * @author yan
     */
    @Component
    public class CDPlayer implements MediaPlayer{
        private CompactDisc cd;
        
        public CDPlayer(CompactDisc cd) {
            super();
            this.cd = cd;
        }
        @Autowired
        public void setCd(CompactDisc cd) {
            this.cd = cd;
        }
        @Override
        public void play() {
            System.out.print("Info of CD playing :");        
            cd.cdInfo();
        }
    
    }

    写一个测试类CDPlayerTest:

     1 package soundSystem;
     2 
     3 import static org.junit.Assert.*;
     4 
     5 import org.junit.Rule;
     6 import org.junit.Test;
     7 import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
     8 import org.junit.runner.RunWith;
     9 import org.springframework.beans.factory.annotation.Autowired;
    10 import org.springframework.test.context.ContextConfiguration;
    11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    12 @RunWith(SpringJUnit4ClassRunner.class)
    13 @ContextConfiguration(classes=CDPlayerConfig.class)
    14 public class CDPlayerTest {
    15     
    16     @Rule
    17     public final StandardOutputStreamLog log=new StandardOutputStreamLog();//需要引入system-rules-1.3.0.jar
    18     
    19     @Autowired
    20     private MediaPlayer player;
    21     
    22     @Autowired
    23     private CompactDisc cd;
    24     
    25     @Test
    26     public void cdShouldNotBeNull() {
    27         assertNotNull(cd);//如果测试正常,说名cd已被初始化,Spring依赖注入有效发挥作用了
    28     }
    29     
    30     @Test
    31     public void play(){
    32         player.play();
    33         assertEquals("Info of CD playing :This CD's title:K歌之王;artist:陈奕迅", log.getLog());
    34     }
    35 }

    需要注意的是,在声明MediaPlayer和CompactDisc时,需要加上自动装配的标志@Autowired,否则无法通过测试。

    此时我们使用扫描的方式实现依赖注入的,也可以不用扫描(将@ComponentScan注释掉,保留@AutoWired),直接在javaConfig中定义bean

     1 package soundSystem;
     2 
     3 import org.springframework.context.annotation.Bean;
     4 //import org.springframework.context.annotation.ComponentScan;
     5 import org.springframework.context.annotation.Configuration;
     6 
     7 @Configuration
     8 //@ComponentScan//如果此处不加任何参数,默认扫描与配置类相同的包
     9 //@ComponentScan("soundSystem")//指定单独的包,这个包内主要放置bean配置
    10 //@ComponentScan(basePackages={"soundSystem","video"})//允许指定多个包,但是,这种用字符串表示的包不安全,可以尝试使用class方式
    11 //@ComponentScan(basePackageClasses={CompactDisc.class,SgrPeppers.class})
    12 public class CDPlayerConfig {
    13 @Bean
    14     public CompactDisc sgtPeppers(){
    15     return new SgtPeppers();
    16 }
    17 @Bean
    18     public CDPlayer cdPlayer(){
    19         return new CDPlayer(sgtPeppers());
    20 }
    21 }

    此时bean缺省id为方法名,也可以自定义(name="***")。

    之所以建议多用javaConfig方式而不是xml方式,主要原因:javaConfig更为强大、类型安全并且对重构友好,因为它就是java代码,就像应用程序中的其他Java代码一样。因此我们可以发挥java提供的所有功能,只要最终得到一个所需实例即可--《Spring实战》中举例为:定义多个同为CD实现类的bean,然后可以通过生产随机数和if判断来控制CDPlayer随机播放。

    以上是使用javaConfig或说注解的方式来实现bean的创建和管理,下面看看XML方式的配置,因为我们已经写了注解,所以直接删除javaConfig类,然后在根目录下创建一个xml文件applicationContext.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:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        ">
    
    <context:component-scan base-package="soundSystem"></context:component-scan>
    </beans>

    这样就实现了和javaConfig类同样的功能。如果删除注解,只用xml来管理则可配置如下:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans
     3     xmlns="http://www.springframework.org/schema/beans"
     4     xmlns:c="http://www.springframework.org/schema/c"
     5     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     6     xmlns:p="http://www.springframework.org/schema/p"
     7     xmlns:context="http://www.springframework.org/schema/context"
     8     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     9     http://www.springframework.org/schema/beans/spring-beans.xsd
    10     http://www.springframework.org/schema/context 
    11     http://www.springframework.org/schema/context/spring-context.xsd
    12     ">
    13     <bean id="compactd" class="soundSystem.SgtPeppers"/>
    14   <bean id="cDPlayer" class="soundSystem.CDPlayer" >
    15     <constructor-arg ref="compactd"></constructor-arg>
    16   </bean>
    17 </beans>

    同时在jUnit测试中将@ContextConfiguration(classes=CDPlayerConfig.class)改为:@ContextConfiguration(locations="/applicationContext.xml")即可实现同样的效果。

    此时我们使用引用的方式将id为compactd的bean引为cDPlayer的构造方法的参数,而compactd的初始值是在java文件中定义的,那么如何通过xml文件来定义初始值呢?

    首先我们删除java文件SgtPeppers.java(CompactDisc接口的实现类)中的属性赋值,即只定义title和artist,并且补充set方法即可,xml中可配置如下:

    1     <bean id="compactd" class="soundSystem.SgtPeppers">
    2         <property name="title" value="K歌之王"></property>
    3         <property name="artist" value="陈奕迅"></property>
    4     </bean>

    这样也可以通过测试。

    其实我们这里是用了setter注入方式,它对应的是(默认的)无参构造,所以会出现一种疏忽,即类中有有参构造而忘了写无参构造时,使用setter注入会报错。

    言归正传,在xml中,还可以将其调整为构造器注入,并赋予初始值,仍然以compactd举例:

    首先在SgtPeppers.java中补充有参构造方法:

    1     public SgtPeppers(String title, String artist) {
    2         super();
    3         this.title = title;
    4         this.artist = artist;
    5     }

    修改bean为:

        <bean id="compactd" class="soundSystem.SgtPeppers">
            <constructor-arg value="K歌之王"></constructor-arg>
            <constructor-arg value="陈奕迅"></constructor-arg><!-- 
            <property name="title" value="K歌之王"></property>
            <property name="artist" value="陈奕迅"></property> -->
        </bean>

    这里的参数顺序必须与定义参数的顺序一致,否则会错,这样不够灵活,不安全,我们可以通过name属性来定义属性名而不受顺序的限制:

    1 <bean id="compactd" class="soundSystem.SgtPeppers">
    2         <constructor-arg name="artist"  value="陈奕迅"></constructor-arg>
    3         <constructor-arg name="title" value="K歌之王"></constructor-arg><!-- 
    4         <property name="title" value="K歌之王"></property>
    5         <property name="artist" value="陈奕迅"></property> -->
    6     </bean>

      另外还有c-命名方式,此处不赘述。

    下面看看bean中如何放置集合:

    先写一个含有集合参数的类:

     1 package soundSystem.collections;
     2 
     3 import java.util.List;
     4 
     5 import soundSystem.CompactDisc;
     6 
     7 public class BlankDisc implements CompactDisc{
     8     private String title;
     9     private String artist;
    10     private List<String> tracks;
    11     /**
    12      * setter方法,对应无参构造,是唯一的,
    13      * 如果有有参构造而没明确标明无参构造,则此处使用setter注入会报错
    14      * @param title
    15      */
    16     public void setTitle(String title) {
    17         this.title = title;
    18     }
    19     public void setArtist(String artist) {
    20         this.artist = artist;
    21     }
    22     
    23     /********************************************************
    24      * 有参构造,不唯一,可以是数量不唯一,也可以是参数类型不唯一,
    25      * 但为保证能够自动装配,此处只定义一种
    26      ********************************************************/
    27     public BlankDisc(String title, String artist, List<String> tracks) {
    28         super();
    29         this.title = title;
    30         this.artist = artist;
    31         this.tracks = tracks;
    32     }
    33     /*重写接口中的方法*/
    34     @Override
    35     public void cdInfo() {
    36         System.out.print("This CD's title:"+title+";artist:"+artist+";tracks:"+tracks);
    37     }
    38 
    39 }

    此类继承CompactDisc接口,并定义了List类型的参数tracks,那么在applicationContext.xml中如何配置:

     1   <bean id="compactd" class="soundSystem.collections.BlankDisc">
     2         <constructor-arg name="artist"  value="陈奕迅"></constructor-arg>
     3         <constructor-arg name="title" value="K歌之王"></constructor-arg>
     4         <constructor-arg name="tracks">
     5             <list>
     6                 <value>1</value>
     7                 <value>2</value>
     8                 <value>3</value>
     9             </list>
    10         </constructor-arg>
    11     </bean>

    当然,list子元素中还可以使用ref,如

    1             <list>
    2                 <ref bean="#ID1"/>
    3                 <ref bean="#ID2"/>
    4                 <ref bean="#ID3"/>
    5             </list>

     当参数类型是List时,我们可以用<list>,同样也可以使用<set>,区别与list和set的区别相同,即不保证顺序,去掉重复值。

     另外一种方法是将list直接放在另外一个bean中,然后在构造参数中引用即可,这里涉及到util:list,需要现在头部文件中加入声明:

     1   <beans
     2     xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:c="http://www.springframework.org/schema/c"
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5     xmlns:p="http://www.springframework.org/schema/p"
     6     xmlns:context="http://www.springframework.org/schema/context"
     7     xmlns:util="http://www.springframework.org/schema/util"
     8     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     9     http://www.springframework.org/schema/beans/spring-beans.xsd
    10     http://www.springframework.org/schema/context 
    11     http://www.springframework.org/schema/context/spring-context.xsd
    12     http://www.springframework.org/schema/util 
    13     http://www.springframework.org/schema/util/spring-util.xsd
    14     ">

    第7,12,13行中已声明,配置bean如下:

     1   <util:list id="ul">
     2         <value>1</value>
     3         <value>2</value>
     4         <value>3</value>
     5     </util:list>
     6      <bean id="compactd" class="soundSystem.collections.BlankDisc">
     7         <constructor-arg name="artist"  value="陈奕迅"></constructor-arg>
     8         <constructor-arg name="title" value="K歌之王"></constructor-arg>
     9         <constructor-arg name="tracks">
    10                 <ref bean="ul"/>
    11         </constructor-arg>
    12     </bean>

    这样就可以起到同样的作用了。

  • 相关阅读:
    AJAX POST请求中参数以form data和request payload形式在servlet中的获取方式
    Java中List集合去除重复数据的方法
    java.util.Date、java.sql.Date、java.sql.Time、java.sql.Timestamp区别和总结
    Spring 中配置log4j日志功能
    log4j配置文件加载方式
    程序中使用log4J打印信息的两种方式
    elasticsearch常用命令
    接私活必备的10个开源项目??
    初识Elasticsearch
    常用在线工具
  • 原文地址:https://www.cnblogs.com/yw0219/p/5988043.html
Copyright © 2020-2023  润新知