Spring中三种装配bean的方式:自动、Java代码及XML装配

推荐使用装配的顺序:自动装配》Java代码》XML装配

一,自动装配

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

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(auto wiring):Spring自动满足bean之间的依赖。
    组件扫描和自动装配组合在一起就可以发挥强大的威力,它们能将你的显示配置降到最低。以光盘和播放器为例,具体看以下代码:
//先定义一个CompactDisc 接口
public interface CompactDisc {
    //具体内容不重要
    public void play();
}
//定义一个CompactDisc实现类
@Component("popularMusic")
public class PopularMusic implements CompactDisc {
    @Override
    public void play() {
        System.out.println("播放流行乐");
    }
}
//
//配置类
@Configuration
@ComponentScan
public class CDPlayerConfig {
    //什么都不用写
}

到目前为止,上述仅仅是一个孤零零的类,什么也没做。那么我们应该如何使用呢?我们有最一般的方法就是new一个对象出来,然后再调用该对象的play()方法即可。但是用new创建对象的方法在实际的业务代码中增加耦合,难以维护,因此才有了Spring。接下来看一下如何用自动装配的方式装配bean(和普通的Java类,Spring中的特殊称谓),继而使用bean的方法。

//单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    private CompactDisc cd;
    @Test
    public void test(){
       cd.play();
    }
}

//测试结果:
播放流行乐

看完所有代码会发现,上述代码和平时自己写的代码没啥区别,仅仅只多了几个注解(带@符号的),但就是这几个注解起到了作用。你自己并没手动创建一个CompactDisc 对象,而事实的确存在,而且成功运行,这一切都由Spring容器完成。
2、几个常用的注解:
@Component: 注解用来说名该类作为一个组件类,并告知Spring要为该类创建bean
@Configuration: 该注解用来说名该类是一个配置类
@ComponentScan: 启用Spring组件扫描(默认不启用),默认扫描与配置类在同一包带 有@ComPonent注解的类。有两个属性:
(1)basePackages:用来设置要进行组件扫描的基础包,值为数组,数组属性为字符串
(2)basePackageClasses: 设置的数组中包含了类,这些类所在的包将作为组件扫描的基础包
@RunWith(SpringJUnit4ClassRunner.class):在测试之前自动生成Spring的应用上下文
@ContextConfiguration(classes = CDPlayerConfig.class):加载配置类。
所以上述代码大致的执行原理如下:
首先Spring容器首先加载配置类,根据配置类中的设置再去扫描指定包中的带有@Component的类;然后Spring容器生成扫描到的所有类的对象(默认是单例的),然后根据需要(哪里有@Autowired)自动装配所需要的bean,至于bean的生命周期则由Spring管理。

二、Java代码装配

要用Java代码来装配bean,那么配置类肯定也是少不了的,一个很简单的配置类定义如下:

@Configuration
public class JavaConfig {
}

这里和上面的配置类不同的地方是这里删除了@ComponentScan注解,因为该注解使用进行设置组件扫描的,而在这里的bean注入是通过Java代码配置的,因此不需要。如果删除之后,再去运行上述的CDPlayerTest的话会报BeanCreationException异常。因为测试期间需要注入的CompactDisc 还未被创建。接下来看一个完整的配置类:

@Configuration
public class JavaConfig {

    /*
     * 声明一个简单的bean
     * */
    @Bean
    public ClassicalMusic classicalMusic(){
        return new ClassicalMusic();
    }
    /*
    * 声明一个带有依赖对象的bean
    * 在这里通过使用构造器的方式来注入,当spring调用该方法时,它会自动装配一个CompactDisc导配置方法中
    * */
    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc){
        return new CDPlayer(compactDisc);
    }
}

@Bean注解告诉Spring这个方法将会返回一个对象,该对象将会注册为Spring应用上下文中的bean。默认情况下bean的ID和方法名一致。
上述代码中生命了两个bean,即classicalMusic和cdPlayer,前者是一个简单的bean,而后者是一个带有依赖对象的bean,当spring调用该方法时,它会自动装配一个CompactDisc导配置方法中。
在这里需要以下三个需要注意的地方:

  • 在声明带有依赖对象的bean时,必须首先声明被依赖的bean,即这里的classicalMusic(ClassicalMusic 实现了CompactDisc )。
  • 这里生成的bean是单例的,若你再声明一个实现了CompactDisc 接口的bean时,则会报错。
  • 带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例(构造器和Setter方法是@Bean方法的简单样例)。

既然上述代码已经配置好了,接下来接看一下如何使用已经配置好的bean,代码如下:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
        CompactDisc compactDisc = (ClassicalMusic)configApplicationContext.getBean("classicalMusic");
        compactDisc.play();
    }
}

//结果
播放古典音乐

上述代码大致的执行原理如下:

通过创建AnnotationConfigApplicationContext 对象来配置后的Spring上下文,然后利用上下文来获取对应的bean,继而使用对应的方法。

三、XML装配

1、借助构造器注入初始化bean
两种基本的配置方式:

  • constructor-arg 元素
  • 使用Spring3.0中引入的c-命名空间

两者的区别在很大程度上就是是否繁琐以及c-命名空间不能注入集合。
下面来看一下一课程-老师的例子做个简单说明:

//课程类
public class Course {
    String courseName;
    double mark;

    public Course(String courseName, double mark) {
        this.courseName = courseName;
        this.mark = mark;
    }
}

//老师类
public class Teacher{

    private Course course;
    public Teacher(Course course){
        this.course = course;
    }
    public void teach() {
        System.out.println("我教的课程:"+course.courseName+" 绩点为:"+course.mark);
    }
}


//配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Course bean-->
    <bean id = "course" class="com.tyf.day4.Course">
        <constructor-arg value="数学" />
        <constructor-arg value="4.0" />
    </bean>

    <!--Teacher bean-->
    <bean id = "teacher" class="com.tyf.day4.Teacher">
        <constructor-arg ref="course" />
    </bean>
</beans>


//测试类
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("com\\tyf\\day4\\ config.xml");
        Teacher teacher = (Teacher) context.getBean("teacher");
        teacher.teach();
    }
}


//测试结果
我教的课程:数学 绩点为:4.0

上述代码仅仅说明了一个使用元素进行构造器注入初始化bean的一个很简单的方法,接下来再看一下如何使用c-命名空间来初始化bean(仅展示xml配置文件)。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Course bean-->
    <bean id = "course" class="com.tyf.day4.Course">
        <constructor-arg value="数学" />
        <constructor-arg value="4.0" />
    </bean>

    <!--Teacher bean-->
    <bean id = "teacher" class="com.tyf.day4.Teacher" c:_0-ref = "course"/>
</beans>

可以看出使用c:-命名空间使得xml配置很简洁,易于读懂。_0说的是构造器中第一个参数,同理_1说的是构造器中第二个参数,一次类推。
那么如果构造器参数列表中有集合类型,则不能使用c:-命名空间,只能使用元素。假如课程中有一个选课学生名字并且使用构造器来注入该属性:

//课程类
public class Course {
    String courseName;
    double mark;
    List<String> students;//选课学生姓名
    public Course(String courseName, double mark,List<String> studenets) {
        this.courseName = courseName;
        this.mark = mark;
        this.students = studenets;
    }
}
//教师类
public class Teacher{

    private Course course;
    public Teacher(Course course){
        this.course = course;
    }
    public void teach() {
        System.out.println("我教的课程:"+course.courseName+" 绩点为:"+course.mark);
        System.out.println("选课学生:");
        for(String name:course.students){
            System.out.println(name);
        }
    }
}
//测试类不变


//xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--Course bean-->
    <bean id = "course" class="com.tyf.day4.Course">
        <constructor-arg value="数学" />
        <constructor-arg value="4.0" />
        <constructor-arg>
            <list>
                <value>Yafeng</value>
                <value>Xiaoshuang</value>
            </list>
        </constructor-arg>
    </bean>
    <!--Teacher bean-->
    <bean id = "teacher" class="com.tyf.day4.Teacher" c:_0-ref = "course"/>
</beans>


//结果
我教的课程:数学 绩点为:4.0
选课学生:
Yafeng
Xiaoshuang

从上述代码可以看出,list 是作为Constructor的一个子元素来进行工作的,list中又有value子元素用来给List中的属性赋值。

2、通过Setter方法注入属性

将Teacher类改为如下(其他不变):

//老师类
public class Teacher implements People {

    private Course course;
    public void setCourse(Course course){
        this.course = course;
    }
    @Override
    public void teach() {
        System.out.println("我教的课程:"+course.courseName+" 绩点为:"+course.mark);
        System.out.println("选课学生:");
        for(String name:course.students){
            System.out.println(name);
        }
    }
}
//XML文件中改变的部分
<!--Teacher bean-->
<bean id = "teacher" class="com.tyf.day4.Teacher">
    <property name="course" ref="course" />
</bean>

这里注入属性的时候用的是property,其它基本和constructor基本一样。而构造器注入有c-命名空间,而Sertter属性注入方法也有一个p-命名空间,用法和c-命名空间一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章