目录
Bean生命周期
阶段1:Bean元信息配置阶段
Bean信息定义4种方式
API的方式
XML的方式
properties文件的方式
注解的方式
小结
阶段2:Bean元信息解析阶段
Bean元信息的解析主要有3种方式
XML方式解析:XmlBeanDefinitionReader
properties文件定义bean的解析: PropertiesBeanDefinitionReader
注解方式:AnnotatedBeanDefinitionReader
阶段3:Spring Bean注册阶段
Bean注册接口:BeanDefinitionRegistry
别名注册接口:AliasRegistry
DefaultListableBeanFactory类
阶段4:BeanDefinition合并阶段
案例
阶段5:Bean Class加载阶段
Bean生命周期
Spring bean 生命周期可以分为13个阶段:
- Bean元信息配置阶段
- Bean元信息解析阶段
- 将Bean注册到容器中
- BeanDefinition合并阶段
- Bean Class加载阶段
- Bean实例化阶段(2个小阶段)
- Bean实例化前阶段
- Bean实例化阶段
- 合并后的BeanDefinition处理
- 属性赋值阶段(3个小阶段)
- Bean实例化后阶段
- Bean属性赋值前阶段
- Bean属性赋值阶段
- Bean初始化阶段(4个小阶段)
- Bean Aware接口回调阶段
- Bean初始化前阶段
- Bean初始化阶段
- Bean初始化后阶段
- 所有单例bean初始化完成后阶段
- Bean的使用阶段
- Bean销毁前阶段
- Bean销毁阶段
阶段1:Bean元信息配置阶段
这个阶段主要就是bean信息的定义
Bean信息定义4种方式
- API的方式
- Xml文件方式
- properties文件的方式
- 注解的方式
API的方式
先来说这种方式,因为其他几种方式最终都会转化为这种方式来定义bean配置信息。
Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构。 不管是是通过xml配置文件的标签,还是通过注解配置的 @Bean ,还是 @Compontent 标注的类,还是扫描得到的类等,它最终都会被解析成一个BeanDefinition对象,最后我们的Bean工厂就会根据这份Bean的定义信 息,对bean进行实例化、初始化等等操作。
你可以把BeanDefinition丢给Bean工厂,然后Bean工厂就会根据这个信息帮你生产一个Bean实例,拿去使用。 BeanDefinition里面里面包含了bean定义的各种信息,如:bean对应的class、scope、lazy信息、 dependOn信息、autowireCandidate(是否是候选对象)、primary(是否是主要的候选者)等信息。
BeanDefinition是个接口,有几个实现类,看一下类图:
BeanDefinition接口
bean定义信息的接口,我们下来看看源码:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
*singleton
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* prototype
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// Modifiable attributes
/**
* 设置此bean的父bean名称(对应xml中bean元素的parent属性)
*/
void setParentName(@Nullable String parentName);
/**
* 返回bean定义时指定的父bean的名称
*/
@Nullable
String getParentName();
/**
* 指定bean的全路径类名(对应xml中的bean的class属性)
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* 返回此 bean 定义的当前 bean 类名。
* 请注意,这不一定是运行时使用的实际类名,以防子定义覆盖/继承其父类的类名。此外,这可能只是
* 调用工厂方法的类,或者在调用方法的工厂 bean 引用的情况下它甚至可能是空的。因此,不要认为
* 这是运行时确定的 bean 类型,而只是在单个 bean 定义级别将其用于解析目的
*/
@Nullable
String getBeanClassName();
/**
* 指定bean的范围(对应xml中bea定义时的scope属性)
*/
void setScope(@Nullable String scope);
/**
* 返回bean的范围
*/
@Nullable
String getScope();
/**
* 设置是否延迟初始化该bean(对应xml中bean定义时的lazy属性)
*/
void setLazyInit(boolean lazyInit);
/**
* 返回该bean是否是一个延迟初始化的bean(只对单例的bean有效,因为多例的bean肯定是使用时才会
* 创建的)
*/
boolean isLazyInit();
/**
* 设置该bean初始化时需要依赖的bean的名称,bean工厂会保证让这些bean在该bean之前初始化
*/
void setDependsOn(@Nullable String... dependsOn);
/**
* 返回此bean依赖的bean的名称
*/
@Nullable
String[] getDependsOn();
/**
* 设置此bean是否作为其他bean自动注入时的候选者
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* 返回此bean是否作为其他bean自动注入时的候选者
*/
boolean isAutowireCandidate();
/**
* 设置此bean是否为自动注入的主要候选者
*/
void setPrimary(boolean primary);
/**
* 返回此bean是否作为自动注入的主要候选者
*/
boolean isPrimary();
/**
* 指定要使用的工厂bean(如果有)。这是要对其调用指定工厂方法的bean的名称
*/
void setFactoryBeanName(@Nullable String factoryBeanName);
/**
* 返回工厂bean名称(如果有)(对应xml中bean元素的factory-bean属性)
*/
@Nullable
String getFactoryBeanName();
/**
*
* 指定工厂方法(如果有)。此方法将使用构造函数参数调用,如果未指定任何参数,则不使用任何参
* 数调用。该方法将在指定的工厂bean(如果有的话)上调用,或者作为本地bean类上的静态方法调
* 用。
*/
void setFactoryMethodName(@Nullable String factoryMethodName);
/**
* 返回工厂方法名称(对应xml中bean的factory-method属性)
*/
@Nullable
String getFactoryMethodName();
/**
* 返回此bean的构造函数参数值
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* 是否有构造器参数值设置信息(对应xml中bean元素的<constructor-arg />子元素)
*/
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
/**
* 获取bean定义时配置的属性值设置信息
*/
MutablePropertyValues getPropertyValues();
/**
* 这个bean定义中是否有属性设置信息(对应xml中bean元素的<property />子元素)
*/
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
/**
* 设置bean初始化方法名称
*/
void setInitMethodName(@Nullable String initMethodName);
/**
* 返回bean初始化方法名称
*/
@Nullable
String getInitMethodName();
/**
* 设置bean销毁方法的名称
*/
void setDestroyMethodName(@Nullable String destroyMethodName);
/**
* 返回bean销毁方法的名称
*/
@Nullable
String getDestroyMethodName();
/**
* 为此BeanDefinition设置角色提示。角色提示为框架和工具提供了特定BeanDefinition的角色和重
* 要性的指示
*/
void setRole(int role);
/**
* 获取此BeanDefinition的角色提示。角色提示为框架和工具提供了特定BeanDefinition的角色和重
* 要性的指示
*/
int getRole();
/**
* 设置bean的描述信息
*/
void setDescription(@Nullable String description);
/**
* 返回Bean的描述信息
*/
@Nullable
String getDescription();
// Read-only attributes
/**
* 返回此 bean 定义的可解析类型
*/
ResolvableType getResolvableType();
/**
* 是否是单例
*/
boolean isSingleton();
/**
* 是否是多例
*/
boolean isPrototype();
/**
* 返回此 bean 是否是“抽象的”,即不打算实例化
*/
boolean isAbstract();
/**
* 返回此bean定义来自的资源的描述(以便在出现错误时显示上下文)
*/
@Nullable
String getResourceDescription();
/**
* 返回原始 BeanDefinition,如果没有则返回null
*/
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
另外,BeanDefinition还实现了两个接口:
-
AttributeAccessor
-
BeanMetadataElement
AttributeAccessor接口:属性访问接口
源码:
public interface AttributeAccessor {
/**
* 设置 属性->值 键值对
*/
void setAttribute(String name, @Nullable Object value);
/**
* 获取某个属性对应的值
*/
@Nullable
Object getAttribute(String name);
/**
* 移除某个属性
*/
@Nullable
Object removeAttribute(String name);
/**
* 是否包含某个属性
*/
boolean hasAttribute(String name);
/**
* 列出所有属性
*/
String[] attributeNames();
}
这个接口相当于key->value数据结构的一种操作,BeanDefinition继承这个,内部实际上是使用了 LinkedHashMap来实现这个接口中的所有方法,通常我们通过这些方法来保存BeanDefinition定义过程中产生的一些附加信息。
BeanMetaDataElement接口
public interface BeanMetadataElement {
@Nullable
default Object getSource() {
return null;
}
BeanDefinition继承这个接口,getSource返回BeanDefinition定义的来源,比如我们通过xml定义 BeanDefinition的,此时getSource就表示定义bean的xml资源;若我们通过api的方式定义 BeanDefinition,我们可以将source设置为定义BeanDefinition时所在的类,出错时,可以根据这个来源方便排错。
RootBeanDefinition类:表示根bean定义信息
通常bean中没有父bean的就使用这种表示。
ChildBeanDefinition类:表示子bean定义信息
如果需要指定父bean的,可以使用ChildBeanDefinition来定义子bean的配置信息,里面有个 parentName 属性,用来指定父bean的名称。
GenericBeanDefinition类:通用的bean定义信息
既可以表示没有父bean的bean配置信息,也可以表示有父bean的子bean配置信息,这个类里面也有 parentName属性,用来指定父bean的名称。
ConfigurationClassBeanDefinition类:表示通过配置类中@Bean方法定义 bean信息
可以通过配置类中使用@Bean来标注一些方法,通过这些方法来定义bean,这些方法配置的bean信息最后会转换为ConfigurationClassBeanDefinition类型的对象。
AnnotatedBeanDefinition接口:表示通过注解的方式定义的bean信息
里面有个方法getMetadata()用来获取定义这个bean的类上的所有注解信息。
BeanDefinitionBuilder:构建BeanDefinition的工具类
spring中为了方便操作BeanDefinition,提供了一个类: BeanDefinitionBuilder ,内部提供了很多 静态方法,通过这些方法可以非常方便的组装BeanDefinition对象。
案例1:组装一个简单的bean
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Test
void test1() {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(User.class.getName());
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
System.out.println(beanDefinition);
}
运行输出:
Root bean: class [com.example.User]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
案例2:组装一个有属性的bean
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Test
void test2(){
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(User.class.getName());
//给该bean的name属性设置值
beanDefinitionBuilder.addPropertyValue("name","张三");
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
System.out.println(beanDefinition);
System.out.println("---------------------");
//创建一个Spring容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//将该Bean注册到Spring容器中
beanFactory.registerBeanDefinition("User",beanDefinition);
//获取容器中的Bean
User user = (User) beanFactory.getBean("User");
System.out.println(user);
}
运行输出:
Root bean: class [com.example.User]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
---------------------
User{name='张三'}
案例3:组装一个有依赖关系的bean
public class User {
private String name;
private Car car;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", car=" + car +
'}';
}
}
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
'}';
}
}
@Test
void test3(){
//创建Car对象,并为其赋值
BeanDefinitionBuilder beanDefinitionBuilder2 = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
beanDefinitionBuilder2.addPropertyValue("name","宝马");
AbstractBeanDefinition CarBeanDefinition = beanDefinitionBuilder2.getBeanDefinition();
//创建User对象,并为其赋值
BeanDefinitionBuilder beanDefinitionBuilder1 = BeanDefinitionBuilder.rootBeanDefinition(User.class.getName());
beanDefinitionBuilder1.addPropertyValue("name","张三");
beanDefinitionBuilder1.addPropertyReference("car","car");//为该bean注入依赖的bean
AbstractBeanDefinition UserBeanDefinition = beanDefinitionBuilder1.getBeanDefinition();
//创建Spring容器,将这两个Bean注册到容器中
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("user",UserBeanDefinition);
beanFactory.registerBeanDefinition("car",CarBeanDefinition);
Car car = (Car) beanFactory.getBean("car");
System.out.println(car);
User user = (User) beanFactory.getBean("user");
System.out.println(user);
}
运行输出:
Car{name='宝马'}
User{name='张三', car=Car{name='宝马'}}
案例4:有父子关系的bean
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Test
void test4(){
//创建一个bean
BeanDefinitionBuilder beanDefinitionBuilder1 = BeanDefinitionBuilder.rootBeanDefinition(User.class.getName());
beanDefinitionBuilder1.addPropertyValue("name","张三");
AbstractBeanDefinition UserBeanDefinition = beanDefinitionBuilder1.getBeanDefinition();
//再创建一个bean,指定该bean的父bean,这样该bean就拥有了父bean的所有属性
BeanDefinitionBuilder beanDefinitionBuilder2 = BeanDefinitionBuilder.childBeanDefinition("user");
AbstractBeanDefinition UserBeanDefinitionSon = beanDefinitionBuilder2.getBeanDefinition();
//将两个bean都注册到容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("user",UserBeanDefinition);
beanFactory.registerBeanDefinition("userSon",UserBeanDefinitionSon);
User user = (User) beanFactory.getBean("user");
System.out.println(user);
User userSon = (User) beanFactory.getBean("userSon");
System.out.println(userSon);
}
运行输出:
User{name='张三'}
User{name='张三'}
注意:
- 有父子关系的时候,我们的 子 创建的不再是rootBeanDifinition对象了,而是childBeanDefinition对象,或者genericBeanDefinition对象,因为只有他们内部才有指定父bean的方法
- 我们知道,一个bean指定了父bean以后,该bean会拥有父bean的所有属性。但是我们再想一想,子bean拥有了父bean的所有属性,那属性值呢?这些属性会有值吗?我们再看看上面的输出,默认情况下子bean也拥有了相同的值。又有一个疑问,那我们可以为子bean的属性再赋值来覆盖之前的值吗,答案是可以的,如图:
案例5:通过api设置(Map、Set、List)属性
主要是用到这几个对象:
- ManagedList
- ManagedSet
- ManagedMap
- ManagedProperties
- RuntimeBeanReference(很重要!!!)
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
'}';
}
}
public class UserObj {
private String name;
private Integer salary;
private Car car;
private List<String> stringList;
private List<Car> carList;
private Set<String> stringSet;
private Set<Car> carSet;
private Map<String,String> stringMap;
private Map<String,Car> stringCarMap;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public List<String> getStringList() {
return stringList;
}
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public List<Car> getCarList() {
return carList;
}
public void setCarList(List<Car> carList) {
this.carList = carList;
}
public Set<String> getStringSet() {
return stringSet;
}
public void setStringSet(Set<String> stringSet) {
this.stringSet = stringSet;
}
public Set<Car> getCarSet() {
return carSet;
}
public void setCarSet(Set<Car> carSet) {
this.carSet = carSet;
}
public Map<String, String> getStringMap() {
return stringMap;
}
public void setStringMap(Map<String, String> stringMap) {
this.stringMap = stringMap;
}
public Map<String, Car> getStringCarMap() {
return stringCarMap;
}
public void setStringCarMap(Map<String, Car> stringCarMap) {
this.stringCarMap = stringCarMap;
}
@Override
public String toString() {
return "UserObj{" +
"name='" + name + '\'' +
", salary=" + salary +
", car=" + car +
", stringList=" + stringList +
", carList=" + carList +
", stringSet=" + stringSet +
", carSet=" + carSet +
", stringMap=" + stringMap +
", stringCarMap=" + stringCarMap +
'}';
}
}
@Test
void test5(){
BeanDefinitionBuilder beanDefinitionBuilder1 = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
beanDefinitionBuilder1.addPropertyValue("name","奥迪");
AbstractBeanDefinition beanDefinition1 = beanDefinitionBuilder1.getBeanDefinition();
BeanDefinitionBuilder beanDefinitionBuilder2 = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
beanDefinitionBuilder2.addPropertyValue("name","宝马");
AbstractBeanDefinition beanDefinition2 = beanDefinitionBuilder2.getBeanDefinition();
ManagedList<String> stringList = new ManagedList<>();
stringList.addAll(Arrays.asList("test1","test2","test3"));
ManagedList<RuntimeBeanReference> carList = new ManagedList<>();
carList.add(new RuntimeBeanReference("car1"));
carList.add(new RuntimeBeanReference("car2"));
ManagedSet<String> stringSet = new ManagedSet<>();
stringSet.addAll(Arrays.asList("test1","test2","test3"));
ManagedSet<RuntimeBeanReference> carSet = new ManagedSet<>();
carSet.add(new RuntimeBeanReference("car1"));
carSet.add(new RuntimeBeanReference("car2"));
ManagedMap<String, String> stringMap = new ManagedMap<>();
stringMap.put("测试1","test1");
stringMap.put("测试2","test2");
stringMap.put("测试3","test3");
ManagedMap<String, RuntimeBeanReference> stringCarMap = new ManagedMap<>();
stringCarMap.put("car1",new RuntimeBeanReference("car1"));
stringCarMap.put("car2",new RuntimeBeanReference("car2"));
BeanDefinitionBuilder beanDefinitionBuilder3 = BeanDefinitionBuilder.rootBeanDefinition(UserObj.class.getName());
beanDefinitionBuilder3.addPropertyValue("name","张三");
beanDefinitionBuilder3.addPropertyValue("salary",50000);
beanDefinitionBuilder3.addPropertyValue("stringList",stringList);
beanDefinitionBuilder3.addPropertyValue("carList",carList);
beanDefinitionBuilder3.addPropertyValue("stringSet",stringSet);
beanDefinitionBuilder3.addPropertyValue("carSet",carSet);
beanDefinitionBuilder3.addPropertyValue("stringMap",stringMap);
beanDefinitionBuilder3.addPropertyValue("stringCarMap",stringCarMap);
AbstractBeanDefinition beanDefinition3 = beanDefinitionBuilder3.getBeanDefinition();
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("car1",beanDefinition1);
factory.registerBeanDefinition("car2",beanDefinition2);
factory.registerBeanDefinition("UserObj",beanDefinition3);
System.out.println(factory.getBean("car1"));
System.out.println(factory.getBean("car2"));
System.out.println(factory.getBean("UserObj"));
}
运行输出:
Car{name='奥迪'}
Car{name='宝马'}
UserObj{name='张三', salary=50000, car=null, stringList=[test1, test2, test3], carList=[Car{name='奥迪'}, Car{name='宝马'}], stringSet=[test1, test2, test3], carSet=[Car{name='奥迪'}, Car{name='宝马'}], stringMap={测试1=test1, 测试2=test2, 测试3=test3}, stringCarMap={car1=Car{name='奥迪'}, car2=Car{name='宝马'}}}
XML的方式
我们一开始学spring的时候使用的就是这种方式,已经很熟悉了,这里不做赘述
properties文件的方式
这种方式很陌生,将bean定义信息放在properties文件中,然后通过解析器将配置信息解析为BeanDefinition对象。
employee.(class)=MyClass // 等同于:<bean class="MyClass" />
employee.(abstract)=true // 等同于:<bean abstract="true" />
employee.group=Insurance // 为属性设置值,等同于:<property name="group" value="Insurance" />
employee.usesDialUp=false // 为employee这个bean中的usesDialUp属性设置值,等同于:<property name="usesDialUp" value="false" />
salesrep.(parent)=employee // 定义了一个id为salesrep的bean,指定父bean为employee,等同于:<bean id="salesrep" parent="employee" />
salesrep.(lazy-init)=true // 设置延迟初始化,等同于:<bean lazy-init="true" />
salesrep.manager(ref)=tony // 设置这个bean的manager属性值,是另外一个bean,名称为tony,等同于:<property name="manager" ref="tony" />
salesrep.department=Sales // 等同于:<property name="department" value="Sales" />
techie.(parent)=employee // 定义了一个id为techie的bean,指定父bean为employee,等同于:<bean id="techie" parent="employee" />
techie.(scope)=prototype // 设置bean的作用域,等同于<bean scope="prototype" />
techie.manager(ref)=jeff // 等同于:<property name="manager" ref="jeff" />
techie.department=Engineering // <property name="department" value="Engineering" />
techie.usesDialUp=true // <property name="usesDialUp" value="true" />
ceo.$0(ref)=secretary // 设置构造函数第1个参数值,等同于:<constructor-arg index="0" ref="secretary" />
ceo.$1=1000000 // 设置构造函数第2个参数值,等同于:<constructor-arg index="1" value="1000000" />
注解的方式
对于注解的方式注册bean的话,常见的就是@Component和@Bean了,这里也不做赘述了。
小结
不管是那一种方式注册bean,最终都是会解析成一个BeanDefinition对象。
阶段2:Bean元信息解析阶段
Bean元信息的解析就是将各种方式定义的bean配置信息解析为BeanDefinition对象
Bean元信息的解析主要有3种方式
- 解析xml方式定义的bean
- 解析properties文件定义的bean
- 解析注解方式定义的bean
XML方式解析:XmlBeanDefinitionReader
spring中提供了一个类 XmlBeanDefinitionReader ,将xml中定义的bean解析为BeanDefinition对 象。
案例:
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
'}';
}
}
public class User {
private String name;
private Car car;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", car=" + car +
'}';
}
}
<?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.example.Car">
<property name="name" value="宝马"></property>
</bean>
<bean id="car1" class="com.example.Car">
<property name="name" value="奥迪"></property>
</bean>
<bean id="car2" parent="car1"></bean>
<bean id="user" class="com.example.User">
<property name="name" value="张三"></property>
<property name="car" ref="car2"></property>
</bean>
</beans>
@Test
public void test1(){
//创建一个spring容器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//定义一个xml方式的bean的读取器,需要传入一个bean的注册器
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);
//根据xml文件的位置解析定义的bean,并将其注册到我们上面指定的spring容器中
String location="applicationContext.xml";
xmlBeanDefinitionReader.loadBeanDefinitions(location);
for (String beanName:factory.getBeanDefinitionNames()){
System.out.println(beanName+"===>"+factory.getBean(beanName));
}
}
运行输出:
car===>Car{name='宝马'}
car1===>Car{name='奥迪'}
car2===>Car{name='奥迪'}
user===>User{name='张三', car=Car{name='奥迪'}}
properties文件定义bean的解析: PropertiesBeanDefinitionReader
spring中提供了一个叫PropertiesBeanDefinitionReader的类,将properties中定义的bean解析为一个BeanDefinition对象
案例:
car.(class)=com.example.Car
car.name=奥迪
car1.(class)=com.example.Car
car1.name=宝马
car2.(parent)=car1
user.(class)=com.example.User
user.name=张三
user.car(ref)=car
@Test
public void test2(){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
PropertiesBeanDefinitionReader propertiesBeanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory);
String location="applicationContext.properties";
propertiesBeanDefinitionReader.loadBeanDefinitions(location);
for (String beanName:beanFactory.getBeanDefinitionNames()){
System.out.println(beanName+"===>"+beanFactory.getBean(beanName));
}
}
运行输出:
user===>User{name='张三', car=Car{name='宝马'}}
car1===>Car{name='奥迪'}
car===>Car{name='宝马'}
car2===>Car{name='宝马'}
其实解析的方式也和xml差不多,只是换成了对应的类来解析bean而已。
注解方式:AnnotatedBeanDefinitionReader
@Primary
public class Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
'}';
}
}
@Lazy
public class User {
@Autowired
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "User{" +
"car=" + car +
'}';
}
}
@Test
public void test3(){
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);
annotatedBeanDefinitionReader.register(Car.class,User.class);
for (String beanName:factory.getBeanDefinitionNames()){
System.out.println(beanName+"===>"+ factory.getBean(beanName));
}
}
运行输出:
car===>Car{name='null'}
user===>User{car=null}
注意:
- 我们所说的注解的解析方式,用到了AnnotatedBeanDefinitionReader类来解析
- 千万千万别理解错了,并不是说要在类上加上@Component注解,才能通过AnnotatedBeanDefinitionReader类来解析这个类。其实@Component注解就没啥用,相当于一个标识符,就是默认情况下看到@Component标注的注解以后,spring就认为我们要解析这个类为一个bean并放到spring容器中
- 可以看到案例里面,注解和xml或者properties方式的解析略有不同,xml或者properties方式是用到了一个叫loadBeanDefinitions()的方法,然后传入文件所在位置。而注解解析的方式是用到了register()方法,传入的参数是需要的类,且是一个可变参数。
- 根据输出,我们可以看到user里面的car为空,为什么啊?明明我们已经用@Autowired注入了啊,这个疑问后面再来解答
阶段3:Spring Bean注册阶段
bean注册阶段需要用到一个非常重要的接口:BeanDefinitionRegistry
Bean注册接口:BeanDefinitionRegistry
该接口中定义了常用的操作bean的方法
先来看看这个接口的源码:
public interface BeanDefinitionRegistry extends AliasRegistry {
/**
* 注册一个新的beanDefinition
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
/**
* 从容器中移除指定名称的bean
*/
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
/**
* 返回该bean的定义信息
*/
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
/**
* 返回容器中是否含有该bean
*/
boolean containsBeanDefinition(String beanName);
/**
* 返回容器中bean的名称列表
*/
String[] getBeanDefinitionNames();
/**
* 返回容器中bean的数量
*/
int getBeanDefinitionCount();
/**
* 返回该bean是否被使用。(beanName可以时名称或者别名)
*/
boolean isBeanNameInUse(String beanName);
}
别名注册接口:AliasRegistry
BeanDefinitionRegistry 接口继承了 AliasRegistry 接口,这个接口中定义了操作bean别名的一些 方法,看一下其源码:
public interface AliasRegistry {
/**
* 为该bean创建一个别名
*/
void registerAlias(String name, String alias);
/**
* 移除该别名
*/
void removeAlias(String alias);
/**
* 返回该别名是否使用
*/
boolean isAlias(String name);
/**
* 返回别名列表
*/
String[] getAliases(String name);
}
DefaultListableBeanFactory类
该类是BeanDefinitionRegistry的实现类
我们打开源码可以看到BeanDefinitionRegistry有很多实现类,那为什么特别提到了其中的DefaultListableBeanFactory实现类你,因为其它实现类的内部都是转发给DefaultListableBeanFactory进行处理的,所以真正的实现BeanDefinitionRegistry这个接口的类是DefaultListableBeanFactory
阶段4:BeanDefinition合并阶段
可能我们定义bean的时候有父子bean关系,此时子BeanDefinition中的信息是不完整的,比如设置属性的时候配置在父BeanDefinition中,此时子BeanDefinition中是没有这些信息的,或者子bean和父bean中都设置了属性,那我们此时的子beanDefinition肯定是不完整的,需要将子bean的 BeanDefinition和父bean的BeanDefinition进行合并,如果有冲突的属性,子bean的属性会覆盖父bean的,经过上面的操作最终就会得到一个 RootBeanDefinition ,合并之后得到的 RootBeanDefinition 包含bean定义的所有信息,包含了从父bean中继继承过来的所有信息,后续bean的所有创建工作就是依靠合并之后BeanDefinition来进行的。 合并BeanDefinition会使用下面这个方法:
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefin ition
bean定义可能存在多级父子关系,合并的时候进进行递归合并,最终得到一个包含完整信息的 RootBeanDefinition
案例
public class Course {
private String courseName;
private String description;
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Course{" +
"courseName='" + courseName + '\'' +
", description='" + description + '\'' +
'}';
}
}
<?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="course" class="com.example.Course"></bean>
<bean id="course1" parent="course">
<property name="courseName" value="java"></property>
<property name="description" value="java核心技术课程"></property>
</bean>
<bean id="course2" parent="course1">
<property name="description" value="javaWeb课程"></property>
</bean>
</beans>
public class mergeBeanDefinitionTest {
@Test
public void test(){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String location="applicationContext.xml";
xmlBeanDefinitionReader.loadBeanDefinitions(location);
for (String beanName:beanFactory.getBeanDefinitionNames()){
System.out.println("-------------------------");
System.out.println(beanName);
System.out.println("合并前");
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
System.out.println(beanDefinition);
for (PropertyValue property:beanDefinition.getPropertyValues()){
System.out.println(property.getName()+"====>"+property.getValue());
}
//调用该方法将父子beanDefinition合并,才能让子的beanDefinition完整
BeanDefinition mergedBeanDefinition = beanFactory.getMergedBeanDefinition(beanName);
System.out.println("合并后");
System.out.println(mergedBeanDefinition);
for (PropertyValue property:mergedBeanDefinition.getPropertyValues()){
System.out.println(property.getName()+"====>"+property.getValue());
}
System.out.println("-------------------------");
}
}
}
运行输出:
-------------------------
course
合并前
Generic bean: class [com.example.Course]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]
合并后
Root bean: class [com.example.Course]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]
-------------------------
-------------------------
course1
合并前
Generic bean with parent 'course': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]
courseName====>TypedStringValue: value [java], target type [null]
description====>TypedStringValue: value [java核心技术课程], target type [null]
合并后
Root bean: class [com.example.Course]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]
courseName====>TypedStringValue: value [java], target type [null]
description====>TypedStringValue: value [java核心技术课程], target type [null]
-------------------------
-------------------------
course2
合并前
Generic bean with parent 'course1': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]
description====>TypedStringValue: value [javaWeb课程], target type [null]
合并后
Root bean: class [com.example.Course]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]
courseName====>TypedStringValue: value [java], target type [null]
description====>TypedStringValue: value [javaWeb课程], target type [null]
-------------------------
重点观察彩色字体部分, course1继承了course,但course本身就没有属性,绿色部分的属性本身就是course1自己的,所有合并前合并后都有这些属性并不奇怪。我们再看看cours2的黄色部分,course2是继承了course1的,course2本身有一个description属性,所以合并之前course2只有一个description属性并不奇怪,但是我们看看合并之后,course2有了courseName属性了,这就是从course1继承过来的属性,这说明了什么?说明就是通过我们调用了getMergeBeanDefinition()以后,子bean和父bean合并,从而让我们的子bean拥有完整的beanDefinition信息,验证了上面的说法!!!还有一点,如果我们在合并如果打印一下调用getMergeBeanDefinition方法返回的对象,就会发现该BeanDefinition对象的类型已经从GenericBeanDefinition变为RootBeanDefinition类型了,并且以后操作都是又这个完整的RootBeanDefinition来完成。
阶段5:Bean Class加载阶段
这个阶段就是将bean的class名称转换为Class类型的对象
BeanDefinition中有个Object类型的字段:beanClass
private volatile Object beanClass;
用来表示bean的class对象,通常这个字段的值有2种类型,一种是bean对应的Class类型的对象,另一 种是bean对应的Class的完整类名,第一种情况不需要解析,第二种情况:即这个字段是bean的类名的时候,就需要通过类加载器将其转换为一个Class对象。 此时会对阶段4中合并产生的 RootBeanDefinition 中的 beanClass 进行解析,将bean的类名转换为 Class对象 ,然后赋值给 beanClass 字段。
源码位置:
org.springframework.beans.factory.support.AbstractBeanFactory#resolveBeanClass文章来源:https://www.toymoban.com/news/detail-435119.html
然后就开始进入下面的实例化这个对象的阶段了。我们下篇文章继续来看!!!文章来源地址https://www.toymoban.com/news/detail-435119.html
到了这里,关于Spring进阶(十六)之spring生命周期的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!