Published on

Spring-Dependencies

Authors
  • avatar
    Name
    Wenzhuo Zhao
    Twitter

A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.

本篇文章介绍注入Bean的方式。

  • 通过构造方式注入Bean
  • 通过set方式注入Bean
  • 集合类Bean的注入
    • List
    • Set
    • Map
    • Properties
  • null值注入
  • 注入时创建内部Bean

通过构造方式注入Bean

假设现在我们有两个类:Bean和AnotherBean,其中Bean依赖于AnotherBean

public class AnotherBean {
}
public class Bean {
    private AnotherBean anotherBean;
    private String text;

    public Bean(AnotherBean anotherBean, String text){
        this.anotherBean = anotherBean;
        this.text = text;
    }
  	@Override
    public String toString() {
        return "Bean{" +
                "anotherBean=" + anotherBean +
                ", text='" + text + '\'' +
                '}';
    }
}

可以看到,在Bean的构造函数中,需要AnotherBean。如何在spring.xml中,配置这种Bean的注入呢?

首先不要忘记引入maven依赖:spring-core, spring-context和单元测试junit

然后在ressources下新建spring.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="AnotherBean" id="anotherBean"/>
</beans>

对AnotherBean,无须多言;对于Bean,我们需要使用<constructor>标签来描述其构造函数。

public Bean(AnotherBean anotherBean, String text){
        this.anotherBean = anotherBean;
        this.text = text;
}

Bean的构造函数中,第一个参数类型为AnotherBean,名为anotherBean;第二个参数类型为java.lang.String,名为text,也就是我们有两个argument,在spring.xml中,添加如下Bean的配置:

<bean class="Bean" id="bean">
        <constructor-arg index="0" name="anotherBean" type="AnotherBean" ref="anotherBean"/>
        <constructor-arg index="1" name="text" type="java.lang.String" value="hello"/>
    </bean>
  • index: 参数的位置,0对应构造函数中第一个参数
  • name: 参数在构造函数中的名字
  • type: 参数的对象类型
  • valueref: 如果参数类型为Java自带的基本类型,则使用value用来赋值。如果参数类型为自定义的,例如AnotherBean类型,则需要在spring.xml中定义,ref指向定义的bean的id

接下来编写我们的测试代码

@Test
public void test(){
  ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
  Bean bean = context.getBean("bean",Bean.class);
  System.out.println("bean = " + bean);
}

得到输出:

bean = Bean{anotherBean=AnotherBean@64f6106c, text='hello'}

能看到我们在spring.xml中设定的值已经成功注入了。

通过setter方式注入

在Bean的原基础上,增加两个属性来供我们实验。这两个属性,是不通过构造函数注入的。

private AnotherBean anotherBean1;
private String text1;

既然要用到setter方式注入,那就不可缺少setter方法。当然,toString方法也需要重写。

public AnotherBean getAnotherBean1() {
  return anotherBean1;
}

public void setAnotherBean1(AnotherBean anotherBean1) {
  this.anotherBean1 = anotherBean1;
}

public String getText1() {
  return text1;
}

public void setText1(String text1) {
  this.text1 = text1;
}

@Override
public String toString() {
  return "Bean{" +
    "anotherBean=" + anotherBean +
    ", text='" + text + '\'' +
    ", anotherBean1=" + anotherBean1 +
    ", text1='" + text1 + '\'' +
    '}';
}

在spring.xml中,用<property>标签来描述这两个属性。

<property name="anotherBean1" ref="anotherBean"/>
<property name="text1" value="world" />
  • name: 属性的名字
  • refvalue: 属性的值,与刚刚的相同。

测试代码无需改动,当然,测试输出的结果会有所改动。

bean = Bean{anotherBean=AnotherBean@4fb64261, text='hello', anotherBean1=AnotherBean@4fb64261, text1='world'}

可以看到"world"被注入进来了。

一种简便写法

在spring.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"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

可以看到,我加入了xmlns:cxmlns:p,那么c代表constructor, p代表property。对于Bean,可以这样重新写:

<bean class="Bean" id="bean"
    c:anotherBean-ref="anotherBean" c:text="hello"
    p:anotherBean1-ref="anotherBean" p:text1="world"/>

与之前的意义是一样的。大家也可以在Intellij IDEA内自己试验一下,看IDE会提示什么。

集合类Bean的注入

在Bean.java新增几个集合类属性来供我们学习。

private List<String> stringList;
private List<AnotherBean> anotherBeanList;

private Set<String> stringSet;
private Set<AnotherBean> anotherBeanSet;

private Map<String, String> stringMap;
private Map<String, AnotherBean> anotherBeanMap;

private Properties properties;

还是用setter方法来注入,因此不要忘记他们的get和set方法,这里就不写了。

在spring.xml内,为我们的这些集合赋值。

<property name="stringList">
  <list>
    <value>first of list</value>
    <value>second of list</value>
  </list>
</property>
<property name="anotherBeanList">
  <list>
    <ref bean="anotherBean"/>
    <ref bean="anotherBean" />
  </list>
</property>

<property name="stringSet">
  <set>
    <value>a value of set</value>
    <value>another value of set</value>
  </set>
</property>
<property name="anotherBeanSet">
  <set>
    <ref bean="anotherBean" />
    <ref bean="anotherBean" />
    <!--实际上写一个就行,因为Set不允许重复元素-->
  </set>
</property>

<property name="stringMap">
  <map>
    <entry key="firstString" value="first of map"/>
    <entry key="secondString" value="second of map"/>
  </map>
</property>
<property name="anotherBeanMap">
  <map>
    <entry key="firstBean" value-ref="anotherBean"/>
    <entry key="secondBean" value-ref="anotherBean" />
  </map>
</property>

<property name="properties">
  <props>
    <prop key="aProp">PROP</prop>
  </props>
</property>

还是相同的,ref要指向配置文件中的Bean对象,value则不用。

前面的不动,测试结果为

bean = Bean{anotherBean=AnotherBean@8e24743, 
            text='hello', 
            anotherBean1=AnotherBean@8e24743, 
            text1='world', 
            
            stringList=[first of list, second of list], 
            anotherBeanList=[AnotherBean@8e24743, AnotherBean@8e24743], 
            stringSet=[a value of set, another value of set], 
            anotherBeanSet=[AnotherBean@8e24743], 
            stringMap={firstString=first of map, 
                       secondString=second of map}, 
            anotherBeanMap={firstBean=AnotherBean@8e24743, secondBean=AnotherBean@8e24743},
            properties={aProp=PROP}}

null值注入

只需要在<property>标签内,加入<null/>

<property name="anotherBean">
	<null/>
</property>

那么这个属性的值也就是null。

注入时创建内部Bean

对于一个属性,类型为自定义的对象,我们通常这样写

<property name="anotherBean1" ref="anotherBean"/>

ref指向在同一配置文件中定义过的Bean的id。但如果我们不想这样指定呢?可以在<property>中新建一个Bean。

<property name="anotherBean1">
   <bean class="AnotherBean"></bean>
</property>

当然,这样新建的类型为AnotherBean的Bean,和其他通过ref指向的AnotherBean,在内存中就不是同一个对象了。