核心容器包含了以下几个模块:
- spring-core
- spring-beans
- spring-context、spring-context-support
- spring-expression
Spring中最核心的两个类:
1. DefaultListableBeanFactory
DefaultListableBeanFactory而是整个bean加载的核心部分,是Spring注册及加载bean的默认实现。
继承关系图
继承关系图
xxxRegistry:是对xxx的增删改查
xxxBeanFactory:bean的工厂
doXXX():xxx方法的真正实现
AbstractXXX类:是对XXX类的默认实现
2. XmlBeanDefinitionReader
XmlBeanFactory继承自DefaultListableBeanFactory,增加了XmlBeanDefinitionReader类型的reader属性,XmlBeanFactory中主要使用该reader属性对资源文件进行读取和注册。
BeanFactory context = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
配置文件读取相关类图
- 通过继承自 AbstractBeanDefinitionReader 中的方法,来使用 ResourceLoader 将资源文件路径转换为对应的 Resource 文件
- 通过 DocumentLoader 对 Resource 文件进行转换,将 Resource 文件转换为 Document 文件
- 通过实现接口 BeanDefinitionDocumentReader 的 DefaultBeanDefinitionDocumentReader 类对 Document 进行解析,并使用 BeanDefinitionParserDelegate 对 Element 进行解析。
配置文件封装
在 Java 中,将不同来源的资源抽象成 URL,通过注册不同的 handler(URLStreamHandler)来处理不同来源的资源的读取逻辑, Spring 对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
URI getURI() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
对不同来源的资源文件都有相应的Resource 实现: FileSystemResource、ClasspathResource、UrlResource、InputStreamResource、ByteArrayResource等。有了Resource接口,便可以对所有资源文件进行统一的处理。
日常开发中,资源文件的加载也是经常用到的,可以直接使用Spring提供的类,比如希望加载文件时可以使用以下代码。
Resource resource = new ClassPathResource("beanFactoryTest.xml");
InputStream inputStream = resource.getInputSream();
默认标签的解析
BeanDefinition
BeanDefinition 是一个接口,在 Spring 中存在三种实现,RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。三种实现均继承了 AbstactBeanDefinition,其中 BeanDefinition 是配置文件中的 <bean> 元素标签在容器中的内部表示形式。<bean>元素标签拥有 class、scope、lazy-init 等配置属性。BeanDefinition则提供了相应的 beanClass、scope、lazyInit属性,BeanDefinition 和 <bean> 中的属性是一一对应的。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
private volatile Object beanClass;
// bean 的作用范围,对应 bean 属性 scope
private String scope = SCOPE_DEFAULT;
// 是否是抽象,对应 abstract
private boolean abstractFlag = false;
// 是否延迟加载,对应 lazy-init
private boolean lazyInit = false;
// 自动注入模式,对应 autowire
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
// 用来表示一个 bean 的实例化依靠另一个 bean 先实例化,对应 depend-on
private String[] dependsOn;
// autowire-candidate 属性设置为 false,这样容器在查找自动装配对象时,
// 将不考虑该 bean,即它不会被考虑作为其他 bean 自动装配的候选者,但是该 bean 本身还是可以
// 使用自动装配来注入其他 bean 的。
// 对应 bean 属性 autowire-candidate
private boolean autowireCandidate = true;
// 自动装配时当出现多个 bean 候选者时,将作为首选者,对应 primary
private boolean primary = false;
// 用于记录 Qualifier,对应子元素 qualifier
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>(0);
// 允许访问非公开的构造器和方法,程序设置
private boolean nonPublicAccessAllowed = true;
...
/**
对应 bean 属性 factory-bean
<bean id="instanceFactoryBean" class="xxx"/>
<bean id="currentTime" factory-bean="instanceFactoryBean" factory-method="createTime"/>
**/
private String factoryBeanName;
// 对应 factory-method
private String factoryMethodName;
// 记录构造函数注入属性,对于 constructor-arg
private ConstructorArgumentValues constructorArgumentValues;
// 普通属性集合
private MutablePropertyValues propertyValues;
// 方法重写的持有者,记录 lookup-method、replaced-method 元素
private MethodOverrides methodOverrides = new MethodOverrides();
// 初始化方法,对应 init-method
private String initMethodName;
// 销毁方法,对应 destory-method
private String destroyMethodName;
// ...各种 set/get
}
- RootBeanDefinition 最常用的实现类,它对应一般性的 <bean> 元素标签
- ChildBeanDefinition 配置文件中的子 <bean>
- GenericBeanDefinition 2.5 新加入的 bean 文件配置属性定义类,是一站式服务类。
Spring 通过 BeanDefinition 将配置文件中的 <bean> 配置信息转换为容器的内部表示,并将这些 BeanDefinition 注册到 BeanDefinitionRegistry 中。Spring 容器的 BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,主要是以 map 的形式保存的,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。
bean的加载
User user = (User)context.getBean("testbean");
大致过程:
- 转换对应 beanName。这里的 beanName 可能是别名,可能是 FactoryBean,所以需要一系列的解析:
- 去除 FactoryBean 的修饰符,也就是如果 name="&aa",那么会首先去除 & 使得 name=aa
- 取指定 alias 所表示的最终 beanName,例如别名 A 指向名称为 B 的 bean 则返回 B;若别名 A 指向别名 B,别名 B 又指向名称为 C 的 bean 则返回 C
- 尝试从缓存中加载单例
单例在 Spring 的同一个容器内只会被创建一次,后续在获取 bean,就直接从单例缓存中获取了。当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从 singletonFactories 中加载。
由于存在依赖注入的问题,所以在 Spring 中创建 bean 的原则是不等 bean 创建完成就会将创建的 ObjectFactory 提早曝光加入到缓存中,一旦下一个 bean 创建需要依赖上一个 bean 则直接使用 ObjectFactory。
Spring 获取 bean 的规则中有这样一条:尽可能保证所有 bean 初始化后都会调用注册的 BeanPostProcesser 的 postProcessAfterInitialization 方法进行处理,在世纪开发过程中可以根据此特性设计自己的逻辑业务。
- bean 的实例化
- 原型模式的依赖检查
只有单例会尝试解决循环依赖。 - 在非 singleton 下检测 parentBeanFactory,看是否需要进入 parentBeanFactory 中加载(当前 BeanFactory 中无该 bean 且 parentBeanFactory 存在且存在该 bean)
- 将存储 XML 配置文件的 GernericBeanDefinition 转换为 RootBeanDefinition。方便 Bean 的后续处理。
- 寻找依赖
- 针对不同的 scope 进行 bean 的创建
- 类型转换(requiredType = true)
特殊的bean: FactoryBean(举例)
一般来说,Spring 是通过反射机制利用 bean 的 class 属性指定实现类来实例化 bean 的。FactoryBean 是为了对付配置 bean 的复杂性的。
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
// 如果返回 true 则 getObject() 时候会将实例放入 Spring 容器中单实例缓存池中
boolean isSingleton();
}
实现了 XxxFactoryBean之后,解析<bean id="xxx" class="xx.xx.XxxFactoryBean" />时候会调用该其实现的 getObject() 方法
循环依赖
- 什么是循环依赖?
循环依赖就是循环引用,就是两个或多个 bean 相互之间持有对方。循环依赖不是循环调用,循环调用是指方法之间的环调用的,循环调用除非有终止条件,否则无法解决。
- 构造器循环依赖
- setter循环依赖
- prototype 范围的循环依赖
- Spring是如何解决循环依赖的?
- 构造器循环依赖
无法解决,抛出 BeanCurrentlyInCreationException 异常.
Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建 bean 过程中发现自己已经在“当前创建bean池”(isSingletonCurrentlyInCreation(beanName))里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从“当前创建bean池”中清除掉。 - setter循环依赖
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
具体步骤如下:
- Spring 容器创建单例 testA bean,首先根据无参构造器创建 bean,并暴露一个 ObjectFactory 用于返回一个提前暴露一个创建中的 bean,并将testA标识符放到 『当前创建 bean 池』,然后进行 setter 注入 testB。
- Spring 容器创建单例testBbean,首先根据无参构造器创建 bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的 bean,并将“testB”标识符放到『当前创建bean池』,然后进行 setter 注入 circle。
- Spring 容器创建单例testC bean,首先根据无参构造器创建 bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的 bean,并将testC标识符放到『当前创建bean池』,然后进行setter注入testA。进行注入testA时由于提前暴露了ObjectFactory工厂,从而使用它返回提前暴露一个创建中的 bean。
- 最后在依赖注入testB和testA,完成 setter 注入。
- prototype 范围的循环依赖
对于 prototype 作用域 bean,Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓存 prototype 作用域的 bean,因此无法提前暴露一个创建中的 bean。
网友评论