Spring容器装配Bean流程
首先Bean的装配有以下四种方式:
- XML Beans
- 注解(@Autowired)
- Java类配置
- Groovy动态语言配置
Bean的配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表;然后根据注册表加载、实例化Bean,并建立Bean和Bean之间的依赖关系;最后将这些准备就绪的Bean放到Bean缓存池中,以供外层的应用进行调用。
基于XML的配置
在Spring低版本中,XML中采用的是基于DTD的配置方式,Spring4.0配置升级后是基于Schema的配置方式,虽然升级是向后兼容的,但后者是我们首选的XML配置方式。
一个基本的基于Schema的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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
- xmlns="http://www.springframework.org/schema/beans" 默认命名空间
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 自定义命名空间,xsi是简称
- xsi:schemaLocation 是个命名空间对应的schema文件
指定命名空间的Schema文件地址有两个用途:XML解析器可以获取Schema文件并对文档进行格式合法性校验;在IDE下可以自动补全代码。
装配一个Bean
在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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="car" class="com.oyty.Car"/>
</beans>
在装配Bean时要指定一个id,这个id是唯一的,外部应用可以通过getBean(id)
获取Bean的实例,如果不指定,Spring将全限定类名dessert.Cake
作为Bean的唯一标识。
Spring支持两种依赖注入方式,属性注入和构造函数注入,属性注入就是通过setXxx()
方法注入Bean的属性值或依赖对象,这种注入方式具有可选择性和灵活性高的优点,所以这是实际开发中最常采用的注入方式。
属性注入
属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射
的方式调用Setter方法注入属性值,实例如下:
public class Car {
private String brand;
private String color;
private int maxSpeed;
public void setBrand(String brand) {
this.brand = brand;
}
public void setColor(String color) {
this.color = color;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
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 id="car" class="com.oyty.Car">
<property name="brand" value="Tesla" />
<property name="color" value="blue" />
<property name="maxSpeed" value="300" />
</bean>
</beans>
Spring只会检查xml中配置的属性有没有对应的setXxx()
方法,至于方法如何实现、有没有对应的属性则不做要求。
构造函数注入
构造函数注入可以保证一些必要的属性在Bean实例化时就得到设置,确保Bean在实例化后就可以使用。
按类型匹配入参
带参的构造函数:
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
xml中的配置:
<bean id="car" class="com.oyty.Car">
<constructor-arg type="java.lang.String" value="Tesla" />
<constructor-arg type="java.lang.Integer" value="300" />
</bean>
按索引匹配入参
我们知道Java语言本身通过入参的类型和顺序区分不同的重载方法,如果Car的构造参数中有两个相同的类型,那么仅通过type就无法确定了,这个时候就要通过入参索引的方式进行确定。
public Car(String brand, String color, int maxSpeed) {
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
<bean id="car" class="com.oyty.Car">
<constructor-arg index="0" value="Tesla" />
<constructor-arg index="1" value="black" />
<constructor-arg index="2" value="300" />
</bean>
联合使用类型和索引入参
如果有下面两个构造方法,那么单一的一种匹配方式就不奏效了:
public Car(String brand, String color, int maxSpeed) {
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
public Car(String brand, String color, double price) {
this.brand = brand;
this.color = color;
this.price = price;
}
这个时候就要联合使用类型和索引来匹配入参:
<bean id="car" class="com.oyty.Car">
<constructor-arg index="0" type="java.lang.String" value="Tesla" />
<constructor-arg index="1" type="java.lang.String" value="black" />
<constructor-arg index="2" type="double" value="1000000" />
</bean>
显示地指定index和type是一种良好的配置习惯
循环依赖问题
如果两个Bean都是构造函数配置,入参相互引用对方的实例,这会发生类似于线程死锁的循环依赖问题,这样Spring容器是无法启动的,解决方法就是将构造函数注入改为属性注入。
注入参数为其它Bean
前面的各种配置方法参数都是基本类型,如果参数是另一个Bean,该如何配置呢?
public class Boss {
private Car car;
public void setCar(Car car) {
this.car = car;
}
}
属性注入依赖其它的Bean:
<bean id="boss" class="com.oyty.Boss">
<property name="car" ref="car" />
</bean>
ref中bean所指定的car
为xml配置中Car的Bean配置的名称。
内部Bean
如果car Bean只被boss Bean引用,而不被容器中任何其它的Bean引用,则可以将car以内部Bean的方式注入到Boss中:
<bean id="boss" class="com.oyty.Boss">
<property name="car">
<bean class="com.oyty.Car">
<property name="brand" value="Tesla" />
<property name="maxSpeed" value="300" />
<property name="color" value="black" />
</bean>
</property>
</bean>
为某一个属性注入null值
如何我们进行如下配置:
<bean id="car" class="com.oyty.Car">
<property name="color"><value></value></property>
</bean>
color属性注入的并不是null值,而是一个空字符串,如果要注入null值,需要使用特定的标签:
<bean id="car" class="com.oyty.Car">
<property name="color"><value></value></property>
<property name="brand"><null/></property>
</bean>
集合类型属性
List & Set
private List favorities = new ArrayList();
public List getFavorities() {
return favorities;
}
public void setFavorities(List favorities) {
this.favorities = favorities;
}
<bean id="boss1" class="com.oyty.Boss">
<property name="favorities">
<list>
<value>看书</value>
<value>打球</value>
<value>赛车</value>
</list>
</property>
</bean>
# 如果favorities是Set类型的话
<bean id="boss1" class="com.oyty.Boss">
<property name="favorities">
<set>
<value>看书</value>
<value>打球</value>
<value>赛车</value>
</set>
</property>
</bean>
Map
private Map jobs = new HashMap();
public Map getJobs() {
return jobs;
}
public void setJobs(Map jobs) {
this.jobs = jobs;
}
<bean id="boss2" class="com.oyty.Boss">
<property name="jobs">
<map>
<entry key="AM" value="回见客户" />
<entry key="PM" value="公司内部会议" />
</map>
</property>
</bean>
Properties
private Properties mails = new Properties();
public Properties getMails() {
return mails;
}
public void setMails(Properties mails) {
this.mails = mails;
}
<bean id="boss3" class="com.oyty.Boss">
<property name="mails">
<props>
<prop key="jobMail">oyty-job@gmail.com</prop>
<prop key="lifeMail">oyty-life@gmail.com</prop>
</props>
</property>
</bean>
通过util命名空间配置集合类型的Bean(略)
使用p标签简化配置
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd " >
<bean id="car1" class="com.oyty.Car"
p:brand="Tesla"
p:color="black"
p:maxSpeed="300"/>
</beans>
Bean的作用域
- singleton 在Spring容器中只存在一个Bean实例,Bean以单例方式存在
- prototype 每次从容器中调用Bean时,都返回一个新的实例
- request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
- session 同一个HTTP session共享一个Bean,比如购物车bean,不同的HTTP session使用不同的bean,该作用域仅适用于WebApplicationContext环境
小结
这篇文章主要讲解了基于XML配置Bean的方式,两种依赖注入方式,属性注入和构造方法注入,分别的配置如何,以及各种类型参数的配置。后面讲解基于注解和Java类的配置。