基础篇——Spring之IOC容器装配Bean
1、Spring装配Bean的过程大致如下:
Spring启动时读取应用程序提供的bean配置信息,并在Spring容器中生成一份该Bean的配置信息注册表,再根据该注册表实例化Bean,装配Bean的属性信息,Bean之间的依赖关系和Bean的行为配置。需要满足的三个条件:Spring框架的相关jar包(Spring容器)、Bean的配置信息、Bean的实现类。
2、bean配置信息的装载方式:XML的配置方式,基于注解的配置方式,基于Java类的配置方式,Groovy动态语言的配置方式。
3、XML配置方式之Schema配置大致包含两部分:一部分需要对用到的命名空间进行声明;另一部分则需要对声明的命名空间进行定义,定义包括命名空间名称和位置。
命名空间的声明:通过xmlns:别名=“命名空间名称”的格式,如下:
//默认命名空间的声明
xmlns="http://www.springframework.org/schema/beans"
//标准命名空间的声明
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
//aop命名空间的声明
xmlns:aop="http://www.springframework.org/schema/aop"
注:默认命名空间没有别名,常用标签<beans></beans>,<bean></bean>
注:aop是自定义命名空间,常用标签<aop:config></aop:config>
命名空间的定义:通过xsi:schemaLocation=“命名空间全称 schema文件位置”的格式,如下:
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"
4、XML配置方式之Spring常用的Schema文件:
spring-beans-4.0.xsd:用于配置Bean; spring-aop-4.0.xsd:用于AOP的配置; spring-tx-4.0.xsd:用于声明式事务的配置; spring-mvc-4.0.xsd:用于MVC的配置; spring-util-4.0.xsd:用于简化某些标准配置; spring-jee-4.0.xsd:用于简化JavaEE中EJB、JNDI等功能的配置; spring-jdbc-4.0.xsd:用于Spring内嵌数据库的配置; spring-jms-4.0.xsd:用于JMS的配置; spring-lang-4.0.xsd:用于集成Groovy等动态语言; spring-oxm-4.0.xsd:用于对象XML映射的配置; spring-task-4.0.xsd:用于任务调度; spring-tool-4.0.xsd:用于集成一些工具。
5、XML配置方式之装配Bean,如下:
<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-4.0.xsd">
<bean id="car" class="com.smart.simple.Car"/>
<bean name="boss" class="com.smart.simple.Boss"/>
</beans>
注:bean的命名可以用id或name,其中id被要求必须以字母开头且不能出现逗号、空格等非结束符号,name没有字符限制。
注:bean可以指定多个名称,如下:
<bean id="car,car1,car2" calss="com.smart.simple.Car"/>
<bean name="boss1,boss2,boss3" calss="com.smart.simple.Boss">
6、XML配置方式之属性注入,大致有三种注入方式:第一种属性注入,第二种构造函数注入,第三种工厂方法注入。
属性注入是通过该属性的setter方法进行注入的方式,一般需要两个条件:一个是需要被注入对象有空参数的构造函数,另一个是需要被注入对象有该属性的setter方法设置值。Spring容器先调用空构造实例化被注入的bean,再调用其setter方法注入属性值,被注入对象示例如下:
package com.smart.ditype
public class Car{
private String color;
//被注入对象的空构造
public Car(){}
//被注入对象属性的setter方法
public void setColor(String color){
this.color = color;
}
}
注:如果类中没有定义任何构造函数,那么JVM会自动生成一个默认的空构造,上述Bean的配置文件如下:
<bean id="car" class="com.smart.ditype.Car">
<property name="color" value="红色">
</bean>
7、XML配置方式之构造函数注入:
构造函数注入是Bean在实例化时以参数的形式注入,大致有三种入参方式:根据类型入参、根据索引入参、类型和索引的联合、通过自身类型的反射入参。被注入对象的有参构造函数如下:
package com.smart.ditype
public class Car{
private String color;
private int maxSpeed;
//被注入对象的有参构造
public Car(String color,int maxSpeed){
this.color = color;
this.maxSpeed = maxSpeed;
}
}
该对象Bean的配置文件如下:
<bean id="Car" class="com.smart.ditype.Car">
<constructor-arg type="java.lang.String">
<value>红色</value>
</constructor-arg>
<constructor-arg type="java.lang.Integer">
<value>200</value>
</constructor-arg>
</bean>
注:在被注入对象的有参构造函数里,参数类型各不相同时,采用类型入参。若是有多个相同类型的参数,一般采取根据索引入参,其有参构造函数如下:
package com.smart.ditype
public class Car{
private String color;
private String type;
//被注入对象的有参构造
public Car(String color,String type){
this.color = color;
this.type = type;
}
}
该对象Bean的配置文件如下:
<bean id="car" class="com.smart.ditype.Car">
<constructor-arg index="0" value="红色"/>
<constructor-arg index="1" value="X-LL130"/>
</bean>
注:在属性注入中Spring会通过属性名找到Bean对象中的setter方法设置属性,而在构造函数注入中Spring不能通过属性名找到构造函数的参数,需要通过类型或索引间接设置参数。若是重载多个参数个数相同的构造函数,被注入对象如下:
package com.smart.ditype
public Car{
private String color;
private String type;
private double price;
private int maxSpeed;
//被注入对象的有参构造
public Car(String color,String type,int maxSpeed){
this.color = color;
this.type = type;
this.maxSpeed = maxSpeed;
}
//重载有参构造
public Car(String color,String type,double price){
this.color = color;
this.type = type;
this.price = price;
}
}
注:上述使用索引入参不能准确的注入到需要的构造函数中,需要在Bean对象的配置文件中联合使用类型入参和索引入参,如下:
<bean id="car" class="com.smart.ditype.Car">
<constructor-arg index="0" value="红色"/>
<constructor-arg index="1" value="x-ll123"/>
<constructor-arg index="3" type="java.lang.Integer" value="200"/>
</bean>
注:这样就能准确的将属性值注入到被注入对象的第一个有参构造中。但是使用有参构造函数注入方式可能出现循环依赖的问题,如下:
package com.smart.ditype
public class Car{
private Boss boss;
//被注入对象注入Bean
public Car(Boss boss){
this.boss = boss;
}
}
package com.smart.ditype
public class Boss{
private Car car;
//被注入对象注入Bean
public Boss(Car car){
this.car = car;
}
}
bean的配置文件如下:
<bean id="car" class="com.smart.ditype.Car">
<constructor-arg index="0" ref="boss"/>
</bean>
<bean id="boss" class="com.smart.ditype.Boss">
<constructor-arg index="0" ref="car"/>
</bean>
注:Spring容器通过构造函数实例化Bean,前提是Bean构造函数引入的参数必须是准备就绪的。当①对象需要注入一个Bean,注入方式采用的是构造函数注入,而这个Bean需要注入①对象,注入方式也采用的是构造函数注入,那么这两个bean都不能通过Spring容器进行实例化,这个问题就是循环依赖,类似于线程死锁的循环。