Bean - 属性注入

一,Bean注入

Bean注入,即为Bean的属性配置值,常用的方法有两种:一是设值注入,二是构造注入。

1.1,设值注入

通常Bean的属性是私有的,同时拥有一组存取器方法,Spring可以借助属性的setter方法,以
实现setter方式注入。

//Student.java

package com.sequence;

public class Student {
    private String name;
    private int age;

    public Student() {
        this.name = "";
        this.age = 0;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return this.age;
    }
}
//Main.java

package com.sequence;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
                 new ClassPathXmlApplicationContext("Beans.xml");
        Student zhangsan = (Student)context.getBean("zhangsan");
        System.out.println("name : " + zhangsan.getName());
        System.out.println("age : " + zhangsan.getAge());
    }
}
//Beans.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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <property name="name" value="zhangsan"/>
        <property name="age" value="20"/>
    </bean>

</beans>

Spring使用<property>元素配置Bean的属性,通过调用属性的setter方法来注入值。Spring容器会使用Student类的默认构造函数来实例化zhangsan,然后使用属性的setter方法来注入值。

注意:<property>元素的value属性设置值时,为它设置整型值与String类型的类并没有区别,都是使用字符串的形式。Spring将根据Bean属性的类型,自动判断value值的正确类型。

1.2,构造注入

使用<constructor-arg>元素,Spring将调用匹配参数的最合适的构造函数。使用构造函数注入,配置文件如下:

//Beans.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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <constructor-arg value="zhangsan"/>
        <constructor-arg value="30"/>
    </bean>

</beans>

1.3,消除构造歧义

使用构造注入时,Spring将试图在Bean中查找对应的构造函数,并传递用于实例化Bean的参数。但是,如果传递的参数可以应用到超过一个构造函数,可能在构造函数匹配中造成歧义。例如:

//Student.java

package com.sequence;

public class Student {
    private String name;
    private String address;
    private int age;
    private double grade;

    public Student(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student(double grade, String name) {
        this.grade = grade;
        this.name = name;
    }
    ...
}
//Beans.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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <constructor-arg value="zhangsan"/>
        <constructor-arg value="30"/>
    </bean>

</beans>
//Main.java

package com.sequence;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
                 new ClassPathXmlApplicationContext("Beans.xml");
        Student zhangsan = (Student)context.getBean("zhangsan");
        System.out.println("name : " + zhangsan.getName());
        System.out.println("age : " + zhangsan.getAge());
        System.out.println("garde : " + zhangsan.getGrade());
        System.out.println("address : " + zhangsan.getAddress());
    }
}

输出结果:

五月 03, 2018 8:48:30 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Thu May 03 20:48:30 CST 2018]; root of context hierarchy
五月 03, 2018 8:48:30 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
name : zhangsan
age : 0
garde : 0.0
address : 30

程序分析:

原本在配置文件中,设置name = “zhangsan”,age=30,但是最终把30设置给了address。
Spring在匹配构造函数时,传入的是两个字符串,此时Student类中刚好有一个构造函数的两
个参数都是字符串,因此,Spring就选用这个构造函数进行注入。最终,把设置给age属性的
值,设置给了address。

1.3.1,使用<constructor-arg>元素的type属性

Beans.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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <constructor-arg type="String" value="zhangsan"/>
        <constructor-arg type="int" value="30"/>
    </bean>

</beans>

输出结果:

五月 03, 2018 9:00:00 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Thu May 03 21:00:00 CST 2018]; root of context hierarchy
五月 03, 2018 9:00:00 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
name : zhangsan
age : 30
garde : 0.0
address : null

程序分析:

使用type属性,指出参数的预期类型。

1.3.2,使用<constructor-arg>元素的index属性

Student类中,有两个构造函数,它们都有两个参数,参数的类型相同,但是参数的顺序不同。
使用下面的配置文件,配置name与age属性,得不到我们想要的结果,例如:

<?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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <constructor-arg type="String" value="zhangsan"/>
        <constructor-arg type="int" value="30"/>
    </bean>

</beans>

输出结果:

五月 03, 2018 9:03:44 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Thu May 03 21:03:44 CST 2018]; root of context hierarchy
五月 03, 2018 9:03:44 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
name : zhangsan
age : 0
garde : 30
address : null

程序分析:

Student类中有两个参数类型相同,但是排列顺序不同的两个构造函数
public Student(String name, int age){...}public Student(int grade, String name){...}。我们原本的目的是要设置name与age属性,但是,实际上设置的是name与grade属性。因为,Spring在内部对每个构造函数与参数进行评分时,没有考虑参数出现在XML中的顺序。这意味着从Spring的角度来看,上面两个构造函数将得到同样的分数。

使用index属性解决上面的问题

//Beans.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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <constructor-arg type="String" index="0" value="zhangsan"/>
        <constructor-arg type="int" index="1" value="30"/>
    </bean>

</beans>

输出结果:

五月 03, 2018 9:15:19 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Thu May 03 21:15:19 CST 2018]; root of context hierarchy
五月 03, 2018 9:15:19 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
name : zhangsan
age : 30
garde : 0
address : null

程序分析:

配置文件中,应用到index属性,明确指出要匹配的构造函数,第一个参数的类型是String类
型,第二个参数的类型是int类型,从输出结果可以看出成功设置name与age属性。

1.4,引用其他Bean

组成应用程序的Bean往往需要互相协作完成应用功能。为了Bean之间的互相访问,必须在
Bean配置文件中指定Bean引用。

1.4.1,使用setter方法指定Bean引用

//Character.java

package com.game;

public class Character {
    private Gun gun;

    public void setGun(Gun gun) {
        this.gun = gun;
    }

    public void kill() {
        gun.shoot();
    }
}
//Gun.java

package com.game;

public class Gun {
    public void shoot() {
        System.out.println("gun shoot!");
    }
}
//Beans.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-3.0.xsd">

    <bean id="gun" class="com.game.Gun" />

    <bean id="goodMen" class="com.game.Character">
        <property name="gun" ref="gun"/>
    </bean>

</beans>
//Main.java

package com.game;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
                 new ClassPathXmlApplicationContext("Beans.xml");
        Character goodMen = (Character) context.getBean("goodMen");
        goodMen.kill();
    }
}

输出结果:

五月 04, 2018 10:34:25 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Fri May 04 10:34:25 CST 2018]; root of context hierarchy
五月 04, 2018 10:34:26 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
gun shoot!

程序分析:

在Bean配置文件中,使用<ref>元素为Bean属性或构造函数指定Bean引用,它的值为要引用的Bean的id。

1.4.2,使用构造函数指定Bean引用

修改前面Character类以及配置文件,如下所示:

//Character.java

package com.game;

public class Character {
    private Gun gun;

    public Character(Gun gun) {
        this.gun = gun;
    }

    public void kill() {
        gun.shoot();
    }
}
//Beans.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-3.0.xsd">

    <bean id="gun" class="com.game.Gun" />

    <bean id="goodMen" class="com.game.Character">
        <constructor-arg ref="gun" />
    </bean>

</beans>

输出结果:

五月 04, 2018 10:48:46 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Fri May 04 10:48:46 CST 2018]; root of context hierarchy
五月 04, 2018 10:48:46 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
gun shoot!

程序分析:

使用<constructor-arg>元素的ref属性,ref设置为要引用的Bean的id。

1.4.3,声明内部Bean

如果Bean实例只用于一个特殊的属性,可以声明为内部Bean。内部Bean声明直接包含在<property>或<constructor-arg>中,不设置任何id或name属性。这样这个Bean将是匿名的,无法在别处使用。实际上,即使为内部Bean定义id或者name属性,也将被忽略。

内部Bean声明包含在<property>

//Beans.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-3.0.xsd">

    <bean id="goodMen" class="com.game.Character">
        <property name="gun">
            <bean class="com.game.Gun" />
        </property>
    </bean>

</beans>

内部Bean声明包含在<constructor-arg>

//Beans.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-3.0.xsd">

    <bean id="goodMen" class="com.game.Character">
        <constructor-arg>
            <bean class="com.game.Gun" />
        </constructor-arg>
    </bean>

</beans>

1.5,装配集合

当配置集合类型的属性时,Spring提供了四种类型的集合配置元素,分别是<list>、<set>、<map>、<porps>

1.5.1,配置<list>、<set>

//Gun.java
package com.game;

public interface Gun {
    public void shoot();
}
//MachineGun.java

package com.game;

public class MachineGun implements Gun{
    public void shoot() {
        System.out.println("MachineGun Shoot!");
    }
}
//ShotGun.java

package com.game;

public class ShotGun implements Gun{
    public void shoot() {
        System.out.println("ShotGun Shoot");
    }
}
//Character.java

package com.game;

public class Character {
    private java.util.List<Gun> gunList;

    public void setGunList(java.util.List<Gun> gunList) {
        this.gunList = gunList;
    }

    public void kill() {
        for(Gun obj : gunList) {
            obj.shoot();
        }
    }
}
//Main.java

package com.game;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
                 new ClassPathXmlApplicationContext("Beans.xml");
        Character badMen = (Character) context.getBean("badMen");
        badMen.kill();
    }
}
//Beans.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-3.0.xsd">

    <bean id="machineGun" class="com.game.MachineGun" />
    <bean id="shotGun" class="com.game.ShotGun" />

    <bean id="badMen" class="com.game.Character">
        <property name="gunList">
            <list>
                <ref bean="machineGun" />
                <ref bean="shotGun" />
            </list>
        </property>
    </bean>

</beans>

输出结果:

五月 04, 2018 4:26:36 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Fri May 04 16:26:36 CST 2018]; root of context hierarchy
五月 04, 2018 4:26:36 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
MachineGun Shoot!
ShotGun Shoot

程序分析:

<list>元素包含一个或多个值,<ref>元素用来定义Spring上下文中的其他Bean引用。还可以使用其他的Spring设置元素作为<list>的成员,包括<value>、<bean>、<null/>

1.5.2、为集合元素指定数据类型

默认情况下,Spring将集合中的所有元素当做字符串看待。如果不打算将集合元素作为字符串使用,就要指定集合中元素的数据类型。可以使用<value>标记的type属性指定每个集合元素的数据类型,也可以使用集合标记的<value-type>属性指定所有元素的数据类型。

1.6,通过工厂方法创建Bean

有时候静态工厂方法是实例化Bean的唯一方法。Spring支持通过<bean>标记的<factory-method>属性来装配工厂创建的Bean。在前面的射击游戏中添加一个地图类Map,这是一个单例类,所有的人物共享同一个地图。Map类与修改后的Character类如下:

//Character.java

package com.game;

public class Character {
    private java.util.List<Gun> gunList;
    private Map map;

    public void setMap(Map map) {
        this.map = map;
    }

    public void useMap() {
        map.useMap();
    }
    ...
}
//Map.java

package com.game;

public class Map {
    private Map() {

    }

    private static class MapSingletonHolder{
        static Map instance = new Map();
    }

    public static Map getInstance() {
        return MapSingletonHolder.instance;
    }

    public void useMap() {
        System.out.println("use the map!");
    }
}
//Beans.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-3.0.xsd">

    <bean id="map" class="com.game.Map" factory-method="getInstance" /> 
    <bean id="machineGun" class="com.game.MachineGun" />
    <bean id="shotGun" class="com.game.ShotGun" />

    <bean id="badMen" class="com.game.Character">
        <property name="map" ref="map" />
        <property name="gunList">
            <list>
                <ref bean="machineGun" />
                <ref bean="shotGun" />
            </list>
        </property>
    </bean>

</beans>
//Main.java

package com.game;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = 
                 new ClassPathXmlApplicationContext("Beans.xml");
        Character badMen = (Character) context.getBean("badMen");
        badMen.useMap();
    }
}

输出结果:

五月 04, 2018 8:06:48 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc5e7b4: startup date [Fri May 04 20:06:48 CST 2018]; root of context hierarchy
五月 04, 2018 8:06:48 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [Beans.xml]
use the map!

1.7,使用表达式装配

在XML配置文件中配置属性的是都是静态定义的,如果为属性配置的值只有在运行期知道,可以使用Spring表达式语言(Spring Expression Language,SpEL)。SpEL是一种强大、简洁的装配Bean的方式,它通过运行期执行的表达式将值装配到Bean的属性。

1.7.1,字面值

最简单的SpEL表达式仅包含字面值,复杂的SpEL表达式也是由简单的表达式构成的。参考Spring实战 - P52。

<?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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <property name="name" value="#{'zhangsan'}" />
        <property name="age" value="#{30}" />
    </bean>

</beans>

#{}标记会提醒Sring这个标记里面的内容是SpEL表达式。

1.7.2,引用Bean

使用SpEL表达式设置静态的值是一件很无聊的事,SpEL表达式能做的另一个基本事情是通过ID引用其他Bean,还可以调用其他Bean的方法。

<?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-3.0.xsd">

    <bean id="zhangsan" class="com.sequence.Student">
        <property name="name" value="zhangsan" />
        <property name="age" value="30" />
    </bean>

    <bean id="lisi" class="com.sequence.Student">
        <property name="name" value="#{zhangsan.name}" />
        <property name="age" value="#{zhangsan.getAge()}" />
    </bean>

</beans>

1.7.3,其他功能

  • 在SpEL中,使用T()运算符会调用类作用域的方法和常量。
  • 在SpEL上执行操作。
  • 在SpEL中筛选集合。

猜你喜欢

转载自blog.csdn.net/cloud323/article/details/80186819
今日推荐