Spring5源码解析-IOC之Scope作用域 一文让你了解bean的作用域scope的底层实现

2023-06-28 14:55:31 浏览数 (2)

前言

我们都知道在Spring中配置Bean的时候有一个属性scope,它默认是singleton,还有prototype、request等其它的scope,之前的文章有了解关于singleton的大致的流程,那么其它的步骤是怎么样的呢?接下来我们进行一下简单的梳理:

scope之singleton

Spring的scope属性默认为singleton,上一篇文章分析了在缓存中获取单例模式的Bean,但是如果缓存中不存在的呢?则需要从头开始加载Bean,这个过程有getSingleton()函数实现:

  • 看源码(AbstractBeanFactory.java)
代码语言:javascript复制
if (mbd.isSingleton()) {
    // 实例化依赖的Bean后对Bean本身进行实例化
    sharedInstance = getSingleton(beanName, () -> {
        try {
            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);
}
  • 源码分析

上一篇文章我们主要讲解了如何在缓存中获取,但是如果缓存中不存在呢?上述代码就诠释了如果缓存中没有,它会如何去做。

我们可以看到上述代码利用了Java 8的新特性 lambda 表达式 () -> ,getSingleton方法的第二个参数为 ObjectFactory< ? > singletonFactory。()-> 相当于创建了一个ObjectFactory类型的匿名内部类,去实现ObjectFactory接口中的getObject()方法,其中** { }** 中的代码相当于写在匿名内部类中getObject()的代码片段,等着getSingleton()方法里面通过ObjectFactory< ? > singletonFactory去显式调用,如:singletonFactory.getObject()。上述的代码可以写成如下格式:

代码语言:javascript复制
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
        }
    }
  }
);

接下来我们进入getSingleton()的方法体中看一下具体实现:

  • 看源码(DefaultSingletonBeanRegistry.java)
代码语言:javascript复制
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 加锁
    synchronized (this.singletonObjects) {
        // 从缓存中检查一遍
        // 因为singleton模式其实就是复用已经创建的,所以这一步很重要必须要检查,
        Object singletonObject = this.singletonObjects.get(beanName);
        // 如果为空,开始加载过程
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                                            "Singleton bean creation not allowed while singletons of this factory are in destruction "  
                                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '"   beanName   "'");
            }
            // 加载前置处理
            beforeSingletonCreation(beanName);
            Boolean newSingleton = false;
            Boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 初始化bean,
                // 这个过程就是 AbstractBeanFactory中 sharedInstance = getSingleton(beanName, () -> {...}) 调用匿名内部类的方法
                // 其实是调用 createBean() 方法
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 后置处理
                afterSingletonCreation(beanName);
            }
            // 加入缓存中
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        // 直接返回
        return singletonObject;
    }
  • 源码解析

上述代码中我们其实可以看到它使用了回调方法,使得程序可以在单例创建的前后做一些准备及处理操作,而真正的获取单例bean的方法其实并不是在此方法中实现的。其实逻辑是在ObjectFactory类型的实例singletonFactory中实现的(即上面的第一段代码)。主要处理操作及内容如下:

  1. 检查缓存是否已经加载过。
  2. 如果没有加载,则记录beanName的正在加载状态
  3. 加载单例前记录加载状态,不要觉得beforeSingletonCreation方法是一个空实现,没有逻辑;其实这个函数做了很重要的操作**记录加载状态,也就是通过this.singletonsCurrentlyInCreation.add(beanName)**将当前正要创建的bean记录在缓存中,这样便可对循环依赖进行检测。具体可以看一下上一篇文章.

一文带你解读Spring5源码解析 IOC之开启Bean的加载,以及FactoryBean和BeanFactory的区别。

  1. 通过调用参数传入的ObjectFactory的个体Object方法实例化Bean
  2. 加载单例Bean后的处理方法调用。同步骤3的记录加载状态相似,当bean加载结束后需要移除缓存中对该Bean的正在加载状态的记录。
  3. 将结果记录在缓存中并且删除加载bean过程中所记录的各种辅助状态。
  4. 返回处理结果。

接着我们在看一下方法addSingleton()

代码语言:javascript复制
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

上面的代码很简单,可以见看出来有一个put,一个get,两个remove。

singletonObjects单例bean的缓存

singletonFactories 单例bean Factory

earlySingletonObjects '早期'创建的单例bean的缓存

registeredSingletons 已经注册的单例缓存

加载完了单例bean后,调用getObjectsForBeanInstance()从bean实例中获取对象,方法也可以看一下上一篇文章:

一文带你解读Spring5源码解析 IOC之开启Bean的加载,以及FactoryBean和BeanFactory的区别。

scope之prototype
  • 看源码(AbstractBeanFactory.java)
代码语言:javascript复制
// 原型模式
else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }
    beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
  • 源码分析

原型模式的初始化过程相对比较简单一些:直接创建一个新的实例就可以了。过程如下:

  1. 调用beforeSingletonCreation()记录加载原型模式bean之前的加载状态,即前置处理
  2. 调用createBean()创建一个bean实例对象
  3. 调用afterSingletonCreation()进行加载原型模式bean后的后置处理
  4. 调用getObjectForBeanInstance()从bean实例中获取对象
其它作用域
代码语言:javascript复制
// 从指定的 scope 模式下创建 Bean 
else {
    String scopeName = mbd.getScope();
    if (!StringUtils.hasLength(scopeName)) {
        throw new IllegalStateException("No scope name defined for bean ´"   beanName   "'");
    }
    Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
        throw new IllegalStateException("No Scope registered for scope name '"   scopeName   "'");
    }
    try {
        // 具体实现在 SimpleThreadScope 下的get
        Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
                return createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
        }
        );
        beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    catch (IllegalStateException ex) {
        throw new ScopeNotActiveException(beanName, scopeName, ex);
    }
}

具体的流程和原型模式基本一样的,只不过是bean实例是由scope.get()实现,如下:

  • 看源码(SimpleThreadScope.java)
代码语言:javascript复制
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
    // 获取 scope 缓存
    Map<String, Object> scope = this.threadScope.get();
    // NOTE: Do NOT modify the following to use Map::computeIfAbsent. For details,
    // see https://github.com/spring-projects/spring-framework/issues/25801.
    Object scopedObject = scope.get(name);
    if (scopedObject == null) {
        scopedObject = objectFactory.getObject();
        // 加入缓存
        scope.put(name, scopedObject);
    }
    return scopedObject;
}

0 人点赞