- Spring解决的核心问题
- Bean与BeanDefinition
- 代码验证BeanDefinition
- 代码运行测试
- Spring容器主要流程
- 与BeanDefinition相关的类
- AttributeAccessor
- BeanMetadataElement
- AbstractBeanDefinition
- RootBeanDefinition
- ChildBeanDefinition
- GenericBeanDefinition
Spring解决的核心问题: 把对象之间的关系用配置来管理
- 依赖注入: 依赖关系在Spring的IOC容器中管理
- 通过把对象包装在Bean中, 以达到管理对象和进行额外操作的目的
Bean 是Spring的一等公民
- Bean 的本质就是Java对象, 这个对象的生命周期由Spring容器来进行管理
- 不需要为了创建Spring的Bean 而在Java类上做额外的限制, 体现了Spring的低侵入.
- 对于Java对象的控制体现在配置上(配置文件或注解)
在Spring中, 是根据配置, 生成用来描述Bean的BeanDefinition. 类似于Java中描述类的Class. BeanDefinition的几个重要属性:
- Bean 的作用范围 @Scope: singleton prototype session globalsession request
- 懒加载lazy-init(@Lazy) : 决定Bean实例是否延迟加载
- 首选primary(@Primary) : 如果一个接口有多个实现类, 那么设置为true的bean,会被选择优先的实现类
- factory-bean(工厂bean的名称) 和factory-method(工厂 方法的名称) (@Configuration和@Bean)
创建一个User类
package com.demo.entity;
public class User {
}
静态工厂类
package com.demo.entity.factory;
import com.demo.entity.User;
//静态工厂调用
public class StaticFactory {
// 静态的方法,返回User对象
public static User getUser(){
return new User();
}
}
实例工厂类
package com.demo.entity.factory;
import com.demo.entity.User;
//实例工厂调用
public class UserFactory {
//普通的方法,返回User对象
//不能通过类名调用,需要通过对象调用
public User getUser(){
return new User();
}
}
xml中配置文件如下
配置文件的解读
- bean的id为user1的, 配置了Scope为单例, 延迟加载, 并且是首选的bean
- id为user2的bean , 定义了一个factory-method 即工厂方法. 之所以能够定义一个工厂方法, 是因为StaticFactory类中, 有
public static User getUser()
静态方法, 可以从该方法中, 通过类名静态的获取Bean的实例. - id为userFactory的bean, 定义了UserFactory这个类, 该类的方法为
public User getUser()
, 由于不是静态的, 因此不能定义工厂方法 - id为user3的, 定义了一个factory-bean, 即工厂bean, 这个bean取自id为userFactory的bean , factory-method工厂方法为UserFactory这个类的getUser方法.
如下的main方法加载了配置文件, 并从中通过getBean方法来获取Bean的实例.
public static void main(String[] args) {
// 加载配置
String xmlPath = "D:\\mycode\\spring_study\\spring-framework-5.2.0.RELEASE\\springdemo\\src\\main\\resources\\spring\\spring-config.xml";
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(xmlPath);
// 获得无参构造函数创建的对象
User user1a = (User) applicationContext.getBean("user1");
User user1b = (User) applicationContext.getBean("user1");
// 静态工厂创建的对象
User user2a = (User) applicationContext.getBean("user2");
User user2b = (User) applicationContext.getBean("user2");
// 实例工厂创建的对象
User user3a = (User) applicationContext.getBean("user3");
User user3b = (User) applicationContext.getBean("user3");
System.out.println("无参构造函数创建的对象user1 "+user1a);
System.out.println("无参构造函数创建的对象user1 "+user1b);
System.out.println("静态工厂创建的对象user2 "+user2a);
System.out.println("静态工厂创建的对象user2 "+user2b);
System.out.println("实例工厂创建的对象user3 "+user3a);
System.out.println("实例工厂创建的对象user3 "+user3b);
}
控制台打印如下 . 可以看到无参构造, 静态工厂, 实例工厂都能够获取到bean , 并且都是单例的. 即多次调用getBean方法, 都是获取的同一个对象. 将配置文件中
scope="singleton"
去除, 即配置文件改成如下的
再次运行main方法, 可以看到通过同样的bean id多次调用getbean方法, 获取到的对象也是一样的. 说明创建Bean默认是单例模式. 将Scope改成
scope="prototype"
再次运行main方法, 可以看到同一个bean id调用getBean方法获取的实例是不同的.
- 读取配置(xml或者注解)到内存中 (解析配置)
- 在内存中, 这些配置会被转化为Resource对象
- Resource对象被解析为BeanDefinition实例
- 把BeanDefinition实例注册到容器中 (通过配置定位对象, 把定位到的对象, 注册到容器中)
BeanDefinition的主要类关系图如下
BeanDefinition
是在spring-beans
模块中的
spring-beans
模块存放的是Bean和简单容器相关的接口和类. BeanDefinition
继承了AttributeAccessor, BeanMetadataElement
这两个接口 . 在Spring中, 某个类继承或实现了某个接口, 也就代表了这个类或者接口有哪些功能.
AttributeAccessor
: 该接口的描述如下, 该接口定义了对任意数据元数据的修改或获取方式.
* Interface defining a generic contract for attaching and accessing metadata
* to/from arbitrary objects.
BeanMetadataElement
BeanMetadataElement
接口, 主要是提供了getSource()
方法. 该方法是用于传输一个可配置的元对象, 主要是返回BeanDefinition
这个对象本身.
Return the configuration source Object for this metadata element (may be null).
AbstractBeanDefinition
AbstractBeanDefinition
抽象类, 实现了BeanDefinition
接口, 里面的方法用于给通用的属性赋值. 其一些方法如下. 在
AbstractBeanDefinition
抽象类基础上, 衍生出了如下的一些子类.
RootBeanDefinition
类中有如下的方法. 该方法说明了RootBeanDefinition
类不能作为其他BeanDefinition类的子类 , 但可以作为父类.
@Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
在DefaultListableBeanFactory
类中有private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit)
方法, 使用到了RootBeanDefinition
. 可以看到是调用了getMergedLocalBeanDefinition
方法用于合并bean 的.
ChildBeanDefinition
是一个子类, 不能单独存在, 必须要依赖于一个父的BeanDefinition. ChildBeanDefinition
目前已经完全被GenericBeanDefinition
取代.
GenericBeanDefinition
是通用的BeanDefinition实现. 是一个Bean文件属性定义类, GenericBeanDefinition
类有如下的setParentName方法, 该方法可以设置parentName, 不会像RootBeanDefinition
去抛异常.
@Override
public void setParentName(@Nullable String parentName) {
this.parentName = parentName;
}