spirng实战(二:装配Bean)

配置Bean容器主要有三种配置机制:

1.在XML中进行显示配置。

2.在java中进行显示配置

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


1.首先我们来看下Spring的自动化装配Bean的方式。Spring从两个角度实现自动化装配:

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

2.自动装配(autowiring):Spring自动满足bean之间的依赖。组件扫描和自动装配的组合能使显示配置降低到最少。

下面贴上通过组件扫描的方式进行创建的Bean(java配置)

package soundsystem;

public interface CompactDisc {
    void play();
}
package soundsystem;

import org.springframework.stereotype.Component;

@Component//声明为组件类
public class SgtPepperss implements CompactDisc {

    private String title ="zzf.";
    private String artist ="the Beatles";
    @Override
    public void play() {
        //return "titile="+title+"by "+artist;
        System.out.println("titile="+title+"by "+artist);
    }
}
package soundsystem;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包
public class CDPlayerConfig {
}
package soundsystem;

import static org.junit.Assert.*;

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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {

    @Autowired
    private CompactDisc compactDisc;

    @Test
    public void cdShouldNotBeNull() {
        //System.out.println(compactDisc.play());
        assertNotNull(compactDisc);
        compactDisc.play();
    }
}

最后输出结果如下:


如果不要java配置文件用xml文件的话只需要添加一个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-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 自动扫描 -->
    <context:component-scan base-package="soundsystem" />
</beans>
然后将classes=XXXX改为xml文件的路径
package soundsystem;

import static org.junit.Assert.*;

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;

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = CDPlayerConfig.class)
@ContextConfiguration({"classpath:soundsystem/CD.xml"})
public class CDPlayerTest {

    @Autowired
    private CompactDisc compactDisc;

    @Test
    public void cdShouldNotBeNull() {
        //System.out.println(compactDisc.play());
        assertNotNull(compactDisc);
        compactDisc.play();
    }
}

以上程序需要额外导入这两个jar包

此时我的目录结构是


1.为组件扫描的bean命名

    Spring应用上下文中所有的bean都会给定一个ID.如在上文定义的SgtPepperss,Spring会自动根据它的类名为其指明一个ID(sgtPepperss)把类的第一个字母变成小写。

    如果想将期望的ID值传递给@Component注解,那么可以将注解配置改为@Component("你期望的ID值")。

    还有一种bean命名方式不使用@Component注解,使用@Named注解来为bean设置ID如@Named(“你期望的ID值”)。Spring支持将@Named作为@Component注解的替代方案,在大多数场景中它们可以互相替换。

2.设置组件扫描的基础包

   只要在@ComponentScan的Value属性中指明包的名称:@ComponentScan(“soundsystem”)如果想更清晰指明所设置的是基础包@ComponentScan(basePackages="soundsystem"),如果想要设置多个基础包只要将属性设置成一个数组即可@ComponentScan(basePackages={"soundsystem","video"})。

    除了将包设置成String类型外另一种方法是将其指定为包中所含的类或接口@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class}),basePackages属性被替换成了basePackageClasses,同时我们会将这些类所在的包作为组件扫描的基础包。

3.通过为bean添加注解实现自动装配

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

    @Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。

并且Setter方法没有什么特殊的地方,@Autowired注解可以用在类的任何方法上。如:

    如果没有匹配的bean,那么在应用上下文创建的时候回抛出一个异常,如果想避免异常出现可以将@Autowired的required属性设置为false:@Autowired(required=false)

    @Autowired是 Spring特有的注解,在java依赖注入规范中@Inject跟@Autowired有细微的差距但是在大多数场景下可以互相替换。


2.通过java代码装配bean

    通常将第三方库中的组件装配到应用中,用组件扫描和自动装配实现是行不通的,因此我们需要明确的配置Spring。这种情况下必须采用显示装配的方式,在显示配置中有两种可选方案:Java和XML方式。

    通过JavaConfig显示配置Spring时的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该在Spring应用上下文中如何创建bean的细节。

    

    此时将@ComponentScan注解移除,再运行上面的代码,测试会失败,并且出现BeanCreationException异常。

1.声明简单的Bean

    在JavaConfig中声明bean需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。@Bean注解会告诉Spring这个方法将返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体包含最终产生bean实例的逻辑。(默认情况下bean的ID与带@Bean注解的方法名是一样的。本例中,bean的名字将会是getSgtPepperss。通过name属性可以指定bean的ID如:@Bean(name="指定的Bean的ID"))。

2.借助JavaConfig实现注入

    我们声明的CDPlayerBean它依赖于CompactDisc,在JavaConfig中装配bean最简单的方式是引用创建bean的方法,如:

@Configuration
//@ComponentScan(basePackages = {"soundsystem"})//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包
public class CDPlayerConfig {

    @Bean
    public CompactDisc getSgtPepperss() {
        return new SgtPepperss();
    }
    @Bean
    public CDPlayer getCDPlayer(){
        return new CDPlayer(getSgtPepperss());
    }
}

CDPlayer类:

public class CDPlayer implements MedisPlayer {

    private CompactDisc cd;

    public CDPlayer(CompactDisc cd) {
        this.cd = cd;
    }

    @Override
    public void play() {
        cd.play();
    }
}

cdPlayer()方法体和sgtPepperss()的区别在于这里没有使用默认的构造器构建实例,而是调用了需要传入CompactDisc对象的构造器来创建CDPlayer实例。看起来CompactDisc是通过sgtPepperss()得到的,事实上因为sgtPepperss()方法上添加了@Bean注解,Spring会拦截所有对它的调用,取保不是每次都对其进行实际的调用。

    默认情况下Spring的bean都是单例的。如:

这两个CDPlayerbean会得到相同的SgtPepperss实例。

    这个CDPlayer()方法请求一个CompactDisc作为参数,当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配一个CompactDisc到配置方法中。然后方法体可以按照合适的方式来使用它。借助这种技术cdPlayer()方法也能将CompactDisc注入到CDPlayer构造器中,并且不用明确引用CompactDisc的@Bean方法。

方式一:

@Configuration
//@ComponentScan(basePackages = {"soundsystem"})//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包
public class CDPlayerConfig {

 /**
    @Bean
    public CDPlayer getCDPlayer(){
        return new CDPlayer(getSgtPepperss());
    }**/
     @Bean
     public CompactDisc getSgtPepperss() {
         return new SgtPepperss();
     }

     @Bean
     public CDPlayer getCDPlayer(CompactDisc compactDisc){
         return new CDPlayer(compactDisc);
     }
}
方式二:
@Configuration
@ComponentScan(basePackages = {"soundsystem"})//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包
public class CDPlayerConfig {

 /**
    @Bean
    public CDPlayer getCDPlayer(){
        return new CDPlayer(getSgtPepperss());
    }**/


     @Bean
     public CDPlayer getCDPlayer(CompactDisc compactDisc){
         return new CDPlayer(compactDisc);
     }
}

    同理通过Setter方法注入CompactDisc的代码:

带有@Bean注解的方法可以采用任何必要的java功能来产生Bean实例,构造器和Setter方法只是@Bean方法的两个简单样例。

3.通过XML装配bean

在使用JavaConfig时候要创建带@Configuration注解的类,在XML配置中药创建一个XML文件,并且以<beans>元素为根。


1.声明一个简单的<bean>

    在xml的Spring配置文件中声明一个bean我们要使用<bean>元素,<bean>元素类似于JavaConfig中的@Bean注解。声明一个CompactDisc bean:

    <bean class="soundsystem.SgtPeppers" />

    创建这个bean的类通过class属性来指定,并且使用全限定的类名。因为没有明确的给定id,所以这个bean将会根据全限定类名来命名。本例中bean的id会是“soundsystem.SgtPeppers#0”其中“#0”是一个计数的形式,用来区分相同类型的其他bean。如果声明了另一个Sgtpeppers,并且没有明确的进行表示,那么他的ID将会是“soundsystem.SgtPeppers#1”。

    通常通过id属性进行对bean的配置:<bean id="compactDisc" class="soundsystem.SgtPeppers" />。

2.借助构造器注入初始化bean

    在Spring XML配置中只有一种声明bean的方式:使用<bean>元素并指定class属性。Spring会从这获取必要的信息来创建bean。在XML中声明DI时时,有两种基本的配置方案:

1.<constructor-arg>元素

2.使用Spring3.0所引入的c-命名空间

此时已经声明了SgtPeppers bean 并且SgtPeppers类实现了CompactDisc接口,所以我们已经有了一个可以注入到CDPlayerbean中的bean。我们所要做的就是在XML中声明CDPlayer并通过ID引用SgtPeppers:


    当Spring遇到这个<bean>元素,它会创建一个CDPlayer实例,<constructor-arg>元素会告知Spring要将一个ID为compactDisc的bean引用传递到CDPlayer的构造器中。

使用c-命名空间:


属性名以“c”开头,也就是命名空间的前缀。接下来是要装配的构造器参数名,在此之后是”-ref”,这是一个命名的约定,它会告诉Spring,正在装配的是一个bean的引用,这个bean的名字是compactDisc,而不是字面量“compactDisc”。

将字面量注入到构造器中:

<?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-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 自动扫描 -->
    <!--<context:component-scan base-package="soundsystem" />-->
    <bean id="compactDisc" class="soundsystem.BlankDisc">
        <constructor-arg value="aaaaaaaaaaaaaaaaaa"/>
        <constructor-arg value="bbbbbbbbbbbbb"/>
    </bean>
</beans>
package soundsystem;

import static org.junit.Assert.*;

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;

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = CDPlayerConfig.class)
@ContextConfiguration({"classpath:soundsystem/CD.xml"})
public class CDPlayerTest {

    @Autowired
    private CompactDisc compactDisc;

   // @Autowired
   // private MedisPlayer medisPlayer;

    @Test
    public void cdShouldNotBeNull() {
        //System.out.println(compactDisc.play());
        assertNotNull(compactDisc);
        //compactDisc.play();
        compactDisc.play();
    }
}

使用c-命名空间将字面量传入构造器:

1.用构造器参数的名字

2.用参数索引装配


装配集合

    
package soundsystem;

import java.util.List;

public class BlankDisc implements CompactDisc {

    private String title;
    private String artist;
    private List<String> tracks;

    public BlankDisc(String title, String artist, List<String> tracks){
        this.title=title;
        this.artist=artist;
        this.tracks=tracks;
    }
    @Override
    public void play() {
            System.out.println(title+" ------ "+artist);
        for (String track:tracks) {
                System.out.println("--"+track);
        }
    }
}
<?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-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 自动扫描 -->
    <!--<context:component-scan base-package="soundsystem" />-->
    <bean id="compactDisc" class="soundsystem.BlankDisc">
        <constructor-arg value="aaaaaaaaaaaaaaaaaa"/>
        <constructor-arg value="bbbbbbbbbbbbb"/>
        <constructor-arg >
            <list>
                <value>"1"</value>
                <value>"111"</value>
                <value>"11111"</value>
                <value>"1111111"</value>
                <value>"11111111111"</value>
            </list>
        </constructor-arg>
    </bean>
</beans>

<list>是<constructor-arg>的子元素,表明一个包含值的列表会传递到构造器中。其中<value>元素用来指定列表中的每个元素。类似的我们可以通过<ref>元素代替<value>元素实现bean引用列表的装配。

如:


装配<set>元素:

<set>与<list>元素的最主要的不同在于当Spring创建要装配的集合时,如果是set所有的重复的值都会被忽略,且存放顺序没有保障。

3.设置属性

    在选择构造器注入和属性注入时有个通用的规则是:对强依赖使用构造器注入,对可选性的依赖使用属性注入。

    此时我们将CDPlayer改成这样:

CDPlayer只有默认构造器,他没有任何强依赖。所以可以采用这种方式声明bean:

创建bean的时候不会有问题。但是测试的时候会出现NullPointerException这个异常。解决方式如下:

<property>元素为属性的Setter方法所提供的功能跟<constructor-arg>元素为构造器所提供的功能使一样的。

Spring为<constructor-arg>元素提供了c-命名空间作为替代方案,与之类似spring也为<property>提供了p-命名空间。启用p-命名空间要在xml文件中与其他命名空间一起对其进行声明:

使用p-命名空间装配compactDisc属性:

假设我们将BlankDisc改成:

public class BlankDisc implements CompactDisc {

    private String title;
    private String artist;
    private List<String> tracks;


    public void setTitle(String title) {
        this.title = title;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public void setTracks(List<String> tracks) {
        this.tracks = tracks;
    }
    @Override
    public void play() {
        System.out.println(title+" ------ "+artist);
        for (String track:tracks) {
            System.out.println("--"+track);
        }
    }
} 

此时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-4.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 自动扫描 -->
    <!--<context:component-scan base-package="soundsystem" />-->
    <!--构造器注入-->
    <!--<bean id="compactDisc" class="soundsystem.BlankDisc">
        <constructor-arg value="aaaaaaaaaaaaaaaaaa"/>
        <constructor-arg value="bbbbbbbbbbbbb"/>
        <constructor-arg >
            <list>
                <value>"1"</value>
                <value>"111"</value>
                <value>"11111"</value>
                <value>"1111111"</value>
                <value>"11111111111"</value>
            </list>
        </constructor-arg>
    </bean>-->

    <!--设值注入-->
    <bean id="compactDisc" class="soundsystem.BlankDisc">
        <property name="title" value="aaaaaaaaaaaaaaaaaa"/>
        <property name="artist" value="bbbbbbbbbbbbb"/>
        <property name="tracks">
            <list>
                <value>"1"</value>
                <value>"111"</value>
                <value>"11111"</value>
                <value>"1111111"</value>
                <value>"11111111111"</value>
            </list>
        </property>
    </bean>
</beans>

最后启动Junit

设值注入成功。

但是不能用p-命名空间啊来装配集合,但是可以使用Spring-utili命名空间来简化BlankDiscbean,首先要在xml中声明util命名空间及其模式:

通过<util:list>来创建列表bean:

Spring-util命名空间的中的元素:

4.导入和混合配置

    在JavaConfig文件中导如别的JavaCofig文件可以用@Import注解来导入如:

或者再创建一个更高级别的JavaConfig文件在这个类中用@Import将两个配置类组合在一起:

那如何加载一个XML配置文件呢?可以用@ImportResource注解,如:

而在XML中则使用<import>元素来引用配置文件


5.小结

   这篇博客讲了Spring装配Bean的三种方式:自动化配置,JavaConfig配置,XML配置。建议尽可能使用自动化配置,在无法进行自动化配置时用JavaConfig配置,XML配置(显示配置),如果对MyBatis的整合。

参考:《Spring实战》

猜你喜欢

转载自blog.csdn.net/qq_37598011/article/details/80614811