一,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中筛选集合。