Spring源码学习心得
由于是第一次阅读源码,文章之中难免存在一些问题,还请包涵指正。
一、 @Autowired与@Resource的区别
用一句话总结两者的区别就是: @Autowired会先通过类型注入,即byType,当存在有多个类型时会通过名称注入。@Resource则相反,会先通过名称注入,即byName,当名称不存在或有多个名称时会通过类型注入。
那么通过名称注入与通过类型注入有什么区别呢?
//创建接口
interface StuService{
String getName();
}
@Service
//Stu2实现接口并注册bean
class Stu2 implements StuService{
@Override
public String getName() {
return "stu2";
}
}
@Service
//Stu3实现接口并注册bean
class Stu3 implements StuService{
@Override
public String getName() {
return "stu3";
}
}
1.1 @Autowired
那么此时如果我们对 StuService注入, @Autowired可以选择注入的类型就有两个,分别是 Stu2与 Stu3。
需要注意的是,类型有很多种选择:
- 当注册bean与获取bean为同一个类时,类型只有这个类本身。
例如,我们有获取session的工具类,需要将其注入到spring之中,
@Component
class SessionUtil{
public String getSession(){
return "session";
}
}
只有一个类,直接注册bean,使用时可以任意选择
@Autowired
SessionUtil sessionUtil;
此时@Autowired只有一个注册类型,直接注入。
- 当注册bean有多个时,类型为所有注册的bean,实现方式有:实现接口、继承、通过其他方式,例如xml配置注册bean。
例如上述 StuService有多个实现类,每个实现类都注册了bean,因此@Autowired可以选择的类型就有两个。
@Autowired
StuService stu;
根据上述的@Autowired逻辑,此时有多个类型,那么会根据bean name查找,(即类名首字母小写的),发现 stu没有对应的实现类,
此时会报错:
Field stu in com.example.demo.spring.Stu1 required a single bean, but 2 were found:
只需要将 stu 替换成 stu2或 stu3即可完成注入。
继承和其他方式同时有多个bean注入时同理。
因此,@Autowired中类型的定义可以归结为:当注册bean有多个时,类型为所有注册的bean,实现方式有:实现接口、继承、通过其他方式,例如xml配置注册bean或者@Bean注册。
1.2 @Resource
- 当只有一个bean时,可以直接注册
@Autowired
SessionUtil sessionUtil;
2. 当有多个bean注册时,如果未指定名称,则bean name为类名首字母小写,指定了bean名称则注册名称为该名称。
例如上文中 Stu1 Stu2都未指定bean名称,因此两者的bean名称分别为 stu1 stu2。
当使用@Bean在方法上注册bean,此时名称为方法名称。
@Bean()
public Student getStudent(){
Student student = new Student();
student.setName("bob");
student.setId(26);
return student;
}
此时该bean名称为 getStudent。
同样,我们也可以注册bean时自定义bean名称
@Bean("stu1")
public Student getStudent(){
Student student = new Student();
student.setName("bob");
student.setId(26);
return student;
}
@Service("stu2")
class Stu2 implements StuService{
@Override
public String getName() {
return "stu2";
}
}
@Component("stu3")
class Stu3 implements StuService{
@Override
public String getName() {
return "stu3";
}
}
在引用时指定bean:
@Resource(name = "stu2")
private StuService stu1;
1.3 @Autowired
当我们使用@Resource时,会根据名称也就是 stu2去查询,此时bean名称只有一个,查到返回
@Resource
private Stu3 stu2;
但是在执行时却发现报错:
Bean named 'stu2' is expected to be of type 'com.example.demo.spring.Stu3' but was actually of type 'com.example.demo.spring.Stu2'
这是因为只根据了bean名称去查询,却没有根据bean类型,查到的是Stu2类型的bean,但是期望的却是Stu3,因此会发生类型不匹配。
二、SpringIOC的Bean注入流程
spring的注册流程主要包含两个部分:
- 容器的启动阶段及预热工作
- Bean的注入流程
先了解一下几个概念:
2.1 概念介绍
2.1.1 配置元数据
存在于磁盘上的项目中用于描述一个bean的数据,可以是xml、properties、yaml等静态文件,也可以是各种注解描述的对应信息,例如@Service、@Component描述的一个bean的信息。
<bean id="role" class="com.wbg.springxmlbean.entity.Role">
<property name="id" value="1"/>
<property name="roleName" value="高级工程师"/>
<property name="note" value="重要人员"/>
</bean>
以上就是一个由xml定义的配置元数据。
2.1.2 BeanDefinition与BeanDefinitionReader
在spring中,无论是那种配置元数据,最终都会转换为BeanDefinition,由BeanDefinition描述要生成并被引用的对象,可以理解为BeanDefinition就是bean的生成模板,或者是bean的说明书,按照BeanDefinition生成bean。
而将配置元数据转换为BeanDefinition的工作就是由BeanDefinitionReader完成的,对于不同的的配置元数据有不同的Reader完成对应的工作,例如有XmlBeanDefinitionReader读取xml配置信息,PropertiesBeanDefinitionReader读取properties配置信息,AnnotatedBeanDefinitionReader读取注解的配置信息。
BeanDefinitionReader的作用就是将磁盘上的文件信息或注解信息转化为内存中用于描述bean的BeanDefinition。
2.1.3 BeanFactoryPostProcessor
BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinitionRegistry中的一个个的BeanDefinition进行一定程度上的修改与替换。例如我们的配置元信息中有些可能会修改的配置信息散落到各处,不够灵活,修改相应配置的时候比较麻烦,这时我们可以使用占位符的方式来配置。例如配置Jdbc的DataSource连接的时候可以这样配置:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="maxIdle" value="${jdbc.maxIdle}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="maxWait" value="${jdbc.maxWait}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
<property name="driverClassName"
value="${jdbc.driverClassName}">
</property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
BeanFactoryPostProcessor就会对注册到BeanDefinitionRegistry中的BeanDefinition做最后的修改,替换$占位符为配置文件中的真实的数据。
2.1.4 BeanDefinitionRegistry
一个存储BeanDefinition的地方,存储方式为KV值,key为beanName,value为BeanDefinition。
2.1.5 容器启动阶段
容器的启动阶段相对比较简单,首先会将存在于各处的磁盘上的配置元信息由各自的Reader读取到内存之中,转换成BeanDefinition,然后注册到BeanDefinationRegistry之中,最后由BeanFactoryPostProcessor进行修改与替换。
2.1.6 BeanFactory与FactoryBean
BeanFactory与FactoryBean的名字很像,但是确实两个不同的东西。
根据命名规则来看,BeanFactory是一个Factory,也就是一个存放bean的工厂,在创建bean完成后放到其中,使用是从其中获取。
而FactoryBean则是一个bean,只不过与不同的的bean不同的是他不仅可以创建本身类型的bean,也可以类似于Factory一样创建一层有包装的新的bean。这个Bean可以返回一个新的类型的bean,在返回之前也可以对其进行加工。
@Component
class FactoryBeanDemo implements FactoryBean<Student>{
@Override
public Student getObject() {
return new Student();
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
}
创建一个FactoryBean只需要实现其接口,并实现其中的两个方法。当我们获取FactoryBean时,会返回其中 getObject()方法返回的对象。而如果想要获取FactoryBean本身,只需要在bean name前加一个"&"符号即可。
@Resource()
private Object factoryBeanDemo;
@GetMapping("/getStu")
private String getBean(){
System.out.println(factoryBeanDemo.getClass());
return stu2.getName();
}
//输出结果
class com.example.demo.domain.Student
可以看到获取到的是Student类型。
class com.example.demo.spring.FactoryBeanDemo
将获取bean名称假“&”符号:
@Resource(name = "&factoryBeanDemo")
private Object factoryBeanDemo;
class com.example.demo.spring.FactoryBeanDemo
可以看到获取到的对象变成了FactoryBeanDemo本身。
2.2 Bean注入流程
在容器启动阶段,已经完成了bean的注册。如果该对象是配置成懒加载的方式,那么直到我们向Spring要依赖对象实例之前,其都是以BeanDefinitionRegistry中的一个个的BeanDefinition的形式存在,也就是Spring只有在我们第一次依赖对象的时候才开启相应对象的实例化阶段。而如果我们不是选择懒加载的方式,容器启动阶段完成之后,其中有一个步骤finishBeanFactoryInitialization(),在这一步将立即启动Bean实例化阶段,通过隐式的调用所有依赖对象的getBean方法来实例化所有配置的Bean,完成类的加载。
doGetBean():获取并返回bean
doGetBean()的主要流程有两个:
- 尝试从缓存中获取bean,如果获取到直接返回。
- 如果没有获取到则尝试加载bean。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
// 1、查询缓存中是否存在,存在的话直接返回
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 根据缓存中的bean获取实例,主要是检测如果是FactoryBean类型,则获取其内部的getObject()的bean。(需要先了解FactoryBean的作用)
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
//2、不存在则创建bean
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// 2.1 尝试从父类的Factory加载bean
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
/*
* 2.2 获取RootBeanDefinition:首先会根据beanName获取BeanDefinition,然后将BeanDefinition转换为RootBeanDefinition
* BeanDefinition 接口的实现类有很多,通过不同方式注册到 BeanDefinitionRegistry 中的 BeanDefinition 的类型可能都不太相同。
最终,在通过 BeanDefinition 来创建 bean 的实例时,通常都会调用 getMergedBeanDefinition 来获取到一个 RootBeanDefinition。
所以,RootBeanDefinition 本质上是 Spring 运行时统一的 BeanDefinition 视图。
* */
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 2.3 初始化依赖的bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
// 2.4 创建实例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
//返回真正的bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
2.2.1 mbd = getMergedLocalBeanDefinition(beanName)获取BeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
BeanDefinition 接口的实现类有很多,通过不同方式注册到 BeanDefinitionRegistry 中的 BeanDefinition 的类型可能都不太相同。 最终,在通过 BeanDefinition 来创建 bean 的实例时,通常都会调用 getMergedBeanDefinition 来获取到一个 RootBeanDefinition。所以,RootBeanDefinition 本质上是 Spring 运行时统一的 BeanDefinition 视图。
此处就是将各种BeanDefinition统一转换为spring能识别的RootBeanDefinition。
2.2.2 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 获取创建好的对象
sharedInstance = getSingleton(beanName, () -> {
try {
//返回真正的bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
从getSingleton()方法中获取创建好的对象
//获取singletonFactory返回的结果
singletonObject = singletonFactory.getObject();
getSingleton()方法中最主要的一次调用也就是从singletonFactory中获取对象,而获取对象的结果就是上面代码中传入的匿名工厂返回的结果,也就是 createBean(beanName, mbd, args)
2.2.3 createBean(beanName, mbd, args) 创建bean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
// 1.解析bean class
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
// 2.准备覆盖的方法
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 3.尝试返回代理创建的Bean,这个作用就是查找bean中所有实现前置和后置处理器的接口,有没有手工创建然后返回的,代替了spring的创建bean的流程
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
//4.真正创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
创建bean主要有以下几步:
- 解析bean的class文件,为后面的根据class文件通过反射创建对象做准备。
- 预处理bean的Override属性,预处理的方式也比较简单,就是在方法prepareMethodOverride中判断一下,如果lookup-method标签或者replaced-method标签中配置了bean中需要覆盖的方法,就将MethodOverride中的overload属性值设置为false。
- 尝试通过反射获取被代理的bean。
- 真正创建bean的过程
2.2.4 Object beanInstance = doCreateBean(beanName, mbdToUse, args) 开始创建bean
以上流程都是获取bean前的流程或获取bean的准备,doCreateBean是真正的创建并填充bean的流程(去掉了一些不重要的代码)。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//1.通过反射创建实例化对象,并将其放入wraaper中。wraaper可以理解为bean的包装对象,里面是bean实例的,还有一些其他bean的属性方便使用
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
//2.允许后处理处理器修改合并后的bean定义,这里只是解析这些@Autowired @Value @Resource @PostConstruct等这些注解,并没有发生实际属性注入的动作
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//3.是否需要提前曝光,用来解决循环依赖时使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
//4.将实例化完成成的bean填充属性
populateBean(beanName, mbd, instanceWrapper);
//5.调用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
//6.循环依赖检查
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
}
}
}
// Register bean as disposable.
//7.注册bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
从上述流程中可以看到,我们创建一个bean主要有以下几个流程:
- 首先通过class根据反射创建对象,此时该对象的所有的属性都为空,可以理解为我们new出的空属性对象。
- 解析@Autowired @Value @Resource @PostConstruct这些注解,但并没有发生属性注入的行为。
- 是否需要提前曝光,用来解决循环依赖时使用,主要作用是如果需要代理会返回代理对象,如果不需要代理,返回前面创建的对象
- 将第一步实例化完成的空属性对象填充属性,其中如果该bean依赖了其他bean,也会在此步骤将依赖的bean装配,如果bean已经被创建,则直接属性注入,如果不存在,则创建bean,创建方式跟本bean相同,可以理解为递归。
- 将实例化完成的bean对象初始化,主要查看bean是否实现了一些前置或后置或初始化的方法,如果是的话就执行。
- 循环依赖检查。
- 根据scope注册bean。
可以看到,经过以上的几个步骤,我们就获取到了一个实例bean。
其中最重要的三个方法:
- 实例化bean
- 装配属性
- 初始化bean
2.2.5 总结
总结来说,创建bean的流程就是先根据反射获取对象,然后填充对象的属性,初始化,最后将bean注册。
2.3 创建bean流程深入理解
上文我们只粗略的讲解了创建bean的过程,并没有深入的查看源码是如何实现的,例如通过反射获取对象是怎么获取的,填充属性是如何填充的,下文将详细阐述2.2.5过程中在源码层面是如何构建的。
2.3.1 instanceWrapper = createBeanInstance(beanName, mbd, args) 获取实例化对象
该方法通过反射获取实例化的空属性对象。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
//1.1解析class
Class<?> beanClass = resolveBeanClass(mbd, beanName);
//1.2确认public权限
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
//2.如果存在 Supplier 回调,则调用 obtainFromSupplier() 进行初始化,因为反射获取对象的效率比较低
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
/*
* 3.如果args为空且方法已经被resolved,则会直接选择对应的构造方法
* mbd.resolvedConstructorOrFactoryMethod的赋值在下方【1】【2】的代码中赋值
* */
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
//4.自动装配的构造方法
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
//5.是否有首选构造方法
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
//6.通过默认的无参构造函数
return instantiateBean(beanName, mbd);
}
0. 首先解析class文件与确认public权限。
- 如果存在 Supplier 回调,则调用 obtainFromSupplier() 进行初始化,因为反射获取对象的效率比较低。
- 如果args为空且使用那个构造函数已经被确定了,则进行标记,后续直接选择使用那种构造方法。
- 如果args不为空或没有被解析过,则选择使用那种构造方法来构造实例化的对象:
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
选择AutowiredAnnotationBeanPostProcessor实现类:
其中重要的代码已贴出:
//1.遍历所有的构造方法
for (Constructor<?> candidate : rawCandidates) {
if (!candidate.isSynthetic()) {
nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
continue;
}
//2.查看当前构造方法是否有@Autowired注解
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found...
}
}
}
//3.如果有@Autowired注解
if (ann != null) {
//4.如果已经有一个@Autowired注解,则说明存在多个@Autowired注解,则抛出异常
if (requiredConstructor != null) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructor: " + candidate +
". Found constructor with 'required' Autowired annotation already: " +
requiredConstructor);
}
boolean required = determineRequiredStatus(ann);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found constructor with 'required' Autowired annotation: " +
candidate);
}
requiredConstructor = candidate;
}
candidates.add(candidate);
}
//5无参构造函数
else if (candidate.getParameterCount() == 0) {
//将其设置为默认构造函数
defaultConstructor = candidate;
}
}
//对上面的处理过程进行判断
//6.1先检查是否有@Autowired注解
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null) {
if (defaultConstructor != null) {
candidates.add(defaultConstructor);
}
else if (candidates.size() == 1 && logger.isInfoEnabled()) {
logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
"': single autowire-marked constructor flagged as optional - " +
"this constructor is effectively required since there is no " +
"default constructor to fall back to: " + candidates.get(0));
}
}
//返回@Autowired注解的构造方法
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
//6.2如果只有一个有参构造函数,则返回该有参函数
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
//6.3对于非Kotlin类只会返回null,所以这里不会进入
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
}
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
candidateConstructors = new Constructor<?>[] {primaryConstructor};
}
else {
//6.4对于不能识别的场景会进入到这里,例如有多个构造函数但是并没有指定@Autowired注解或者没有构造函数(java会帮我们生成一个无参的构造函数),返回null
candidateConstructors = new Constructor<?>[0];
}
2-5步会对所有的构造函数进行检查,并在检查完进行标记,并会在第6步对标记的结果进行返回,按照ifelse判断顺序主要分为以下几种情况:
- 如果有@Autowired注解的方法则返回该构造方法
- 如果只有一个有参构造函数则会返回该有参构造函数
- 对于不能识别的场景会进入到这里,例如有多个构造函数但是并没有指定@Autowired注解或者没有构造函数(java会帮我们生成一个无参的构造函数)会返回null
在获取到需要的构造函数后,会进行标记,下次不用再次解析可以直接选用那个构造函数,即上文的第4步
- 是否有首选的构造函数
- 如果都没有的话,通过默认的无参构造函数创建对象。
我们查看代码发现,无论第4步返回什么结果,最终会执行以下两个方法:
autowireConstructor()与instantiateBean()
两者都会调用
instantiate()方法
最终都会执行以下这个方法
BeanUtils.instantiateClass(constructorToUse)
也就是如下的代码
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class<?> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
return ctor.newInstance(argsWithDefaultValues);
其中最重要的一句:
return ctor.newInstance(argsWithDefaultValues);
可以发现,也就是这里通过反射的方式创建了一个空属性对象,并一层层返回,直到后面的属性装配等过程,可以说这里就是bean加载过程的源头。
2.3.2 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) 解析各种注解
该方法主要解析该bean所相关的注解,例如属性有@Resource,bean中@PostConstruct注解都会被解析。
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
processor主要有两个实现类:
- AutowiredAnnotationBeanPostProcessor 处理@Autowired和@Value注解bean定义信息
- CommonAnnotationBeanPostProcessor 处理@Resource、@PostConstruct、@PreDestroy注解的bean定义信息
这里需要注意的是,该方法只是会解析并不会真正的进行注入,因为学习意义不大,并不在赘述。
2.3.3 populateBean(beanName, mbd, instanceWrapper) 对实例化完成的bean进行属性注入
//遍历所有的属性
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
//对属性进行装填
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
其中 bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName)有几个实现方法,比较重要的是:
- AutowiredAnnotationBeanPostProcessor,主要装配属性是@Autowired与@Value的属性
- CommonAnnotationBeanPostProcessor,主要装配属性是@Resource的属性
两者最终都会进入如下方法:
//判断要注入的是属性还是方法
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
//如果是属性的话则直接注入
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
//否则通过反射注入
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
理解起来比较简单,判断是方法注入还是属性注入,在注入时注入的对象为:
getResourceToInject(target, requestingBeanName)
找到ResourceElement的实现方法中getResource()方法:
返回了 autowireResource(this.resourceFactory, element, requestingBeanName)
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
else {
resource = factory.getBean(name, element.lookupType);
autowiredBeanNames = Collections.singleton(name);
}
在这个方法中,无论是if还是else,最终都会调用
getBean(name, element.lookupType)
也就是我们bean注入的入口,这个过程很像递归,在我们创建bean时,如果发现我们有依赖的其他bean,那么就会去创建依赖的bean,如果依赖的bean还有其依赖的属性则又会去创建被依赖的属性,只到最终全部创建完成,返回一开始想要创建的bean。
2.3.4 exposedObject = initializeBean(beanName, exposedObject, mbd)初始化bean
在该方法中,会对已经填充过属性的bean进行初始化:
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//对bean的前置处理,其中@PostConstruct就在此步骤中
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//调用初始化方法如果bean实现了InitializingBean接口,则先执行InitializingBean接口的afterPropertiesSet方法,然后执行xml或注解设置的init-method方法。
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//对bean进行后置处理,对象的代理发生在此步骤中
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
在初始化bean的时候,主要分为三个部分,分别是applyBeanPostProcessorsBeforeInitialization、invokeInitMethods、applyBeanPostProcessorsAfterInitialization,分别对应于初始化的前置处理、自定义init方法、后置处理。
applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization两个方法的大概逻辑就是获取获取所有实现其接口的类,然后执行其中被覆盖的方法。
常用的注解执行顺序如下:
- @PostConstruct注解修饰的方法
- InitializingBean接口的afterPropertiesSet()方法
- init-method指定的方法
- @PreDestroy注解修饰的方法
- DisposableBean接口的destroy()方法
- destory-method指定的方法
并且在代码中可以看到,前置处理与后置处理都可以改变bean。
在容器启动阶段我们讲到BeanFactoryPostProcessor,这里我们讲到BeanPostProcessor,那么BeanFactoryPostProcessor 和 BeanPostProcessor 有什么区别呢?
BeanFactoryPostProcessor存在于容器启动阶段,而BeanPostProcessor存在于对象实例化阶段,BeanFactoryPostProcessor关注对象被创建之前那些配置的修改,而BeanPostProcessor阶段关注对象已经被创建之后的功能增强,替换等操作,这样就很容易区分了。 BeanPostProcessor与BeanFactoryPostProcessor都是Spring在Bean生产过程中强有力的扩展点。Spring中著名的AOP(面向切面编程),其实就是依赖BeanPostProcessor对Bean对象功能增强的。
BeanFactoryPostProcessor主要用于解决实例化之前,对实例的属性进行拓展,而BeanPostProcessor是在实例化之后对对象做的拓展。
2.4 总结
用简单的话描述一下,创建一个bean的过程大概包括三部分:
- 通过反射实例化bean
- 属性装配以及填充
- 初始化,包括init-method、以及其前后三个步骤。其中AOP增强就是发生在初始化之后的
applyBeanPostProcessorsAfterInitialization的步骤中。
通过以上的步骤,就可以获得我们可以正常使用的一个bean。
三、Spring如何通过三级缓存策略解决循环依赖问题
3.1 解决循环依赖过程
3.1.1 三级缓存的作用
循环依赖在我们日常开发中属于比较常见的问题,spring对循环依赖做了优化,使得我们在无感知的情况下帮助我们解决了循环依赖的问题。
最简单的循环依赖就是,A依赖B,B依赖C,C依赖A,如果不解决循环依赖的问题最终会导致OOM,但是也不是所有的循环依赖都可以解决,spring只可以解决通过属性或者setter注入的单例bean,而通过构造器注入或非单例模式的bean都是不可解决的。
通过上文创建bean的过程中我们知道,在获取bean的时候,首先会尝试从缓存中获取,如果从缓存中获取不到才会去创建bean,而三层的缓存正是解决循环依赖的关键:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
//从一级缓存中加载
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//从二级缓存中加载
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference为true代表要去三级缓存中查找,此时为true
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//从三级缓存中加载
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
可以看到三层缓存其实就是三个hashmap:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
三级缓存的作用:
- singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
- earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
- singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
3.1.2 解决循环依赖的流程
根据上文,我们都知道创建bean的流程主要包括以下三步:
- 实例化bean
- 装配bean属性
- 初始化bean
例如我们现在有 A依赖B,B依赖A,那么spring是如何解决三层循环的呢?
- 首先尝试从一级缓存中加载A,发现A不存在
- 实例化A(没有属性,半成品)
- 将实例化完成的A放入第三级缓存中
- 装配属性B(没有属性,半成品)
- 尝试从缓存中加载B,发现B不存在
- 实例化B
- 将实例化完成的B放入第三级缓存中
- 装配属性A
- 尝试从缓存中加载A,发现A存在于缓存中(第3步),将A从第三级缓存中移除,放入第二级缓存中,并将其赋值给B,B装配属性完成
- 此时B的装配属性完毕,初始化B,并将B从三级缓存中移除,放入一级缓存
- 返回第四步,此时A的属性也装配完成
- 初始化A,并将A放入一级缓存
自此,实例A于B都分别完成了创建的流程。
用一张图来描述:
那么此时有一个问题,在第9步中B拥有的A是只实例化完成的对象,并没有属性装配以及初始化,A的初始化是在11步以后,那么在最后全部创建完成此时B中的的属性A是半成品还是已经可以正常工作的成品呢?答案是成品,因为B对A可以理解为引用传递,也就是说B中的属性A于第11步之前的A为同一个A,那么A在第11步完成了属性装配,自然B中的属性也会完成属性装配。
例如我们在一个方法中传入一个实例化对象,如果在方法中对实例化对象做了修改,那么在方法结束后该实例化对象也会做出修改,需要注意的是实例化对象,而不是java中的几种基本对象,基本对象是属于值传递(其实实例化对象也是值传递,不过传入的是对象的引用,可以理解为引用传递)。
private static void fun3(){
Student student = new Student();
student.setName("zhangsan");
System.out.println(student.getName());
changeName(student);
System.out.println(student.getName());
String str = "zhangsan";
System.out.println(str);
changeString(str);
System.out.println(str);
}
private static void changeName(Student student){
student.setName("lisi");
}
private static void changeString(String str){
str = "lisi";
}
//输出结果
zhangsan
lisi
zhangsan
zhangsan
可以看出引用传递会改变对象,而值传递不会。
3.2 为什么是三层缓存
3.2.1 三级缓存在循环依赖中的作用
有的小伙伴可能已经注意到了,为什么需要三层缓存,两层缓存好像就可以解决问题了,然而如果不考虑代理的情况下确实两层缓存就能解决问题,但是如果要引用的对象不是普通bean而是被代理的对象就会出现问题。
大家需要知道的是,spring在创建代理对象时,首先会实例化源对象,然后在源对象初始化完成之后才会获取代理对象。
我们先不考虑为什么是三级缓存,先看一下在刚才的流程中代理对象存在什么问题
回到我们刚刚举的例子,加入现在我们需要代理对象A,其中A依赖于B,而B也是代理对象,如果不进行特殊处理的话会出现问题:
- 首先尝试从缓存中加载A,发现A不存在
- 实例化A(没有属性,半成品)
- 将实例化完成的A放入第三级缓存中
- 装配属性B(没有属性,半成品)
- 尝试从缓存中加载B,发现B不存在
- 实例化B
- 将实例化完成的B放入第三级缓存中
- 装配属性A
- 尝试从缓存中加载A,发现A存在于缓存中(第3步),将A从第三级缓存中移除,放入第二级缓存中,并将其赋值给B,B装配属性完成
- 此时B的装配属性完毕,初始化B,并将B从三级缓存中移除,放入一级缓存
- 返回第四步,此时A的属性也装配完成
- 初始化A,并将A放入一级缓存
跟之前一样的流程,那么此时B拥有的对象是A的普通对象,而不是代理对象,这就有问题了。
可能有同学会问,不是存在引用传递吗?A被代理完成不是还是会被B所拥有吗?
但是答案也很简单,并不是,A跟A的代理对象肯定时两个对象,在内存中肯定也是两个地址,因此需要解决这种情况。
3.2.2 解决代理对象的问题
我们来看看spring是如何解决这个问题的:
根据bean创建的过程我们知道,bean会首先被实例化,在实例化完成之后会执行这样一段代码:
//3.是否需要提前曝光,用来解决循环依赖时使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
这也是我们2.2.5中的代码,主要有两部分
首先会判断是否需要提前曝光,判断结果由三部分组成,分别是:
- 是否是单例模式,默认情况下都是单例模式,spring也只能解决这种情况下的循环依赖
- 是否允许提前曝光,默认是true,也可以更改
- 是否正在创建,正常来说一个bean在创建开始该值为true,创建结束该值为false
可以看到正常情况下一个bean的这些结果都为true,也就是会进入到下面的方法中,该方法中有一个lamda表达式,为了可读性这里进行了拆分。
ObjectFactory<Object> objectFactory = new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
} ;
addSingletonFactory(beanName, objectFactory);
该方法的主要内容就是创建了一个ObjectFactory,其中getObject()方法返回了getEarlyBeanReference(beanName, mbd, bean)这个方法调用结果。
看看addSingletonFactory()这个方法都干了什么
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//向三级缓存中添加内容
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
其中最主要的一行代码就是向三级缓存中添加了对象,而添加的对象就是传入的objectFactory,注意,此处添加的不是bean,而是factory。这也是我们上文解决循环依赖过程中第三步的操作。
假如存在循环依赖的话,此时A已经被加入到缓存中,当A被作为依赖被其他bean使用时,按照我们之前的逻辑会调用缓存
//从三级缓存中加载
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//从刚刚添加的objectFactory获取其中的对象,也就是调用getObject,也就是获取getEarlyBeanReference方法的内容
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
从刚刚添加的objectFactory获取其中的对象,也就是调用getObject,也就是获取getEarlyBeanReference方法的内容
那么我们看看为什么会返回一个factory而不是一个bean
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
return wrapIfNecessary(bean, beanName, cacheKey);
根据链路可以看到,最终调用到这个方法中返回的,也就是说当实例A被别的bean依赖时,返回的其实是这个方法中的结果。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 如果是一个需要被代理对象的话,会在此处返回被代理的对象
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
看到这里应该就是一目了然了spring是如何处理存在代理对象且存在循环依赖的情况的。
回到之前的逻辑,例如此时实例B需要装填属性A,会从缓存中查询A是否存在,查询到A已经存在,则调用A的getObject()方法,如果A是需要被代理的对象则返回被代理过得对象,否则返回普通bean。
此外还需要注意的是:先创建对象,再创建代理类,再初始化原对象,和初始化之后再创建代理类,是一样的,这也是可以提前暴露代理对象的基础。
3.2.3 二级缓存的作用
那么二级缓存是干什么用的呢?
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
可以在源代码中看出每调用一次getObject()然后调用getEarlyBeanReference()中 createProxy()都会产生一个新的代理对象,并不不符合单例模式。
(网上有很多文章说是因为调用lamda表达式所以会产生新的对象,其实如果非代理bean并不会产生新的对象,因为objectFactory所持有的是有原始对象的,即时多次调用也会返回相同的结果。但是对于代理对象则会每次新create一个,所以其实会产生新的代理对象而不会是新产生普通对象。所以就其本质为什么使用二级缓存的原因是因为创建代理对象是使用createProxy()的方法,每次调用都会产生新的代理对象,那么其实只要有一个地方能根据beanName返回同一个代理对象,也就不需要二级缓存了,这也是二级缓存的本质意义。其实也可以在getObject()方法中去缓存创建完成的代理对象,只不过这样做就太不优雅,不太符合spring的编码规范。 )
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
例如A依赖于B,B依赖与A、C、D,而C、D又依赖于A,如果不进行处理的话,A实例化完成之后,在B创建过程中获取A的代理对象A1,然后C、D获取的代理对象就是A2、A3,显然不符合单例模式。
因此需要有一个地方存储从factory中获取到的对象,也就是二级缓存的功能:
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//存储获取到的代理对象或普通bean
this.earlySingletonObjects.put(beanName, singletonObject);
//此时三级缓存的工厂已经没有意义了
this.singletonFactories.remove(beanName);
}
3.2.4 代理对象什么时候被初始化的
本来以为到这里就结束了,但是在梳理有代理对象的循环依赖时,突然又发现一个问题:
还是A、B都有代理互相依赖的例子,在B装配完A的代理对象后,B初始化完成,A开始初始化,但是此时的A是原始beanA1,并不是代理对象A2,而B持有的是代理对象A2,那么原始对象A1初始化A2并没有初始化,这不是有问题的吗?
在经过一天的查找以及搜寻资料还有debug后,终于在一篇文章中到答案:
不会,这是因为不管是
cglib代理还是jdk动态代理生成的代理类,内部都持有一个目标类的引用,当调用代理对象的方法时,实际会去调用目标对象的方法,A完成初始化相当于代理对象自身也完成了初始化
也就是说原始对象A1初始化完成后,因为A2是对A1的封装以及增强,也就代表着A2也完成了初始化。
3.2.5 什么时候返回代理对象
此外还有一点需要注意的是,在A1装配完之后,以后其他bean依赖的应该是A2,并且加入到一级缓存中的也应该是A2,那么什么时候A1被换成A2的呢?
//在上方已经判断过,一般为true
if (earlySingletonExposure) {
//1.注意此处传入的是false,并不会去三级缓存中查找,并且如果是代理对象的话此时会返回代理对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//2.判断经过后置处理器后对象是否被改变 ==的话说明没有被改变 那么如果是代理对象的话返回被代理的bean
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
以上有两个需要注意的地方:
- 因为在之前在B装配属性A的时候,即从三级缓存中查找A的时候,如果查到了会将A的bean(有可能是代理对象)放入二级缓存,然后删除三级缓存,那么此时
getSingleton()返回的就是二级缓存中的bean。 - 这里判断经过后置处理器的bean是否被改变,一般是不会改变的,除非实现
BeanPostProcessor接口手工改变。如果没有改变的话则将缓存中的数据取出返回,也就是说如果此时二级缓存中的是代理beanA2,此时会返回A2而不是原始对象A1,如果是普通bean的话则都一样。而如果对象已经被改变则走下面的判断有没有可能报错的逻辑。
3.2.6 循环依赖总结
在学习代理在循环依赖的,发现其实并不太需要二级缓存,可以在bean实例化完成之后就选择要不要生成代理对象,如果要生成的话就往三级缓存中放入代理对象,否则的话就放入普通bean,这样别人过来拿的时候就不用判断是否需要返回代理对象了。
后面发现在网络上有很多跟我想得一样的人,目前参考别人的想法以及自己进行了总结大概是这样子的:
无论是实例化完成之后就进行对象代理还是选择返回一个factory在使用的时候进行代理其实效率上都没有什么区别,只不过一个是提前做一个是滞后做,那么为什么spring选择滞后做的这件事呢?我自己的思考是:
道理也很简单,既然效率没有什么提升的话,为什么要破坏普通bean的创建流程,本来循环依赖就是一件非常小概率的事,没必要因为小概率事情并且滞后也可以解决,从而选择需要修改普通bean的创建逻辑,这无异于是本末倒置,而这也是二级缓存或者说三级缓存中存放的是factory的意义。
四、关于第一次阅读源码后的一些思考
关于为什么使用三级缓存去解决循环依赖的问题,我整整思考了两天才有了一些头绪。其实三级缓存解决循环依赖是非常容易理解的,但是不理解的是为什么一定要使用三级缓存,最困难的还是学习代理在循环依赖中的问题,如何解决循环依赖倒是简单了很多。印象深刻的是自己在学习代理是如何解决的时候一直没有搞懂,本着学不会就不回家的念头在晚上十一点半也没有搞懂,此处手动@女朋友,在我学习的时候一直陪着我,虽然大多时候都在说一些屁话,但是还是很开心有她!还帮我点了一份炸鸡O。o!
其实作为学习计划一开始并没有想学这么深入,本来只是打算在使用程度就够了,但是随着对源码的越来越熟悉,就像回到了高中时代看悬疑小说。从一开始的无从下手,到静下心认真去开始看,到遇到一个不理解的问题,到最终通过自己的努力去在源码中找到答案,随即开怀大笑、成就感爆满,就像是破案一样,一点一点的去找到问题的答案,而最终的结局就是自己对框架的理解以及自己编码方面的一些提升。
最享受的莫过于困扰了一个两天的问题,始终想不通为什么这样也是可以,终于在看到某篇文章后恍然大悟,道一句”原来如此!“,折服于作者的思想与编码技术,说是酣畅淋漓也丝毫不为过。如此一直循环往复,遇见问题、解决问题、成就感.......读完spring的整篇源码,与我而言,丝毫不亚于读完一篇完美的悬疑小说,有枯燥的开头、平淡的发展、热烈的高潮和最终的结局。对我而言,这是十分幸运的。
本篇文章大概1.4w字,参考博文差不多有百余篇,虽然之前也写过一些博文,但也是第一次写这么长的总结。希望自己以后仍能保持这份心态,不被年龄所打败,不被社会所折服!借用习大大的话就是”不忘初心,方得始终“。
五、参考文章
blog.csdn.net/a745233700/… spring的加载流程
www.cnblogs.com/elvinle/arc… doCreateBean - createBeanInstance
blog.csdn.net/xiaomengxin… spring源码对属性赋值 populateBean(注解)
blog.csdn.net/u012098021/… Spring 循环依赖及三级缓存
daimingzhi.blog.csdn.net/article/det… 面试必杀技,讲一讲Spring中的循环依赖