Spring IOC 源码解析之getBean(三)

Spring5.1.x版本,使用注解扫描的方式

接上一篇Spring IOC 源码解析之refresh(二),继续分析getBean方法。


该方法就是用来获取bean的,如果不存在,就会进行创建。

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

/**
 * 返回bean实例,若没有则先实例化
 * @return an instance of the bean
 * @throws BeansException if the bean could not be created
 */
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    // 1、获取一个正规的beanName,处理两种情况,一个是前面说的 FactoryBean(前面带 ‘&’),
	// 一个是别名问题,因为这个方法是 getBean,获取 Bean 用的,你要是传一个别名进来,是完全可以的
    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    // 2、从单例map中尝试获取bean,看是否已经存在
    // (如果存在,那该实例可能是一个完整的bean,也可能是已经实例化,但还没初始化的bean)
    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 + "'");
            }
        }
        // 3、如果是普通 Bean 的话,直接返回 sharedInstance,
        // 如果是 FactoryBean 的话,返回它创建的那个实例对象
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // 进入这里表示bean实例还不存在,即将要去创建
    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        // 4、针对prototype的bean,表明有循环依赖,prototype不能有循环依赖
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        BeanFactory parentBeanFactory = getParentBeanFactory();
        // 5、检查一下这个 BeanDefinition 在容器中是否存在,如果父容器存在并且BeanDefinition
        // 在当前容器不存在,则去父容器中查找
        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) {
            // 6、将bean标记为已创建(或将要创建)
            markBeanAsCreated(beanName);
        }

        try {
            // 7、由于在上一步可能清除了beanName的合并bean定义,所以这里重新获取合并bean定义
            // 并检查该合并bean定义
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // Guarantee initialization of beans that the current bean depends on.
            // 8、先初始化要依赖的Bean,这里指的是depends-on 中定义的依赖,不是作为属性的依赖
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // depends-on 不允许循环依赖,因为depends-on就是来决定实例化顺序的,
                    // 循环依赖了就不知道先实例化谁
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // 为给定的bean注册一个从属bean,要在给定的bean被销毁之前将其销毁
                    registerDependentBean(dep, beanName);
                    try {
                        // 先初始化依赖项
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // 9、创建单例
            // 再从单例缓存中获取实例,若没获取到,则从ObjectFactory创建单例bean实例
            if (mbd.isSingleton()) {
                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;
                    }
                });
                // 如果是普通 Bean 的话,直接返回 sharedInstance,如果是 FactoryBean 的话,返回它创建的那个实例对象
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // 10、创建prototype实例
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    // 创建之前先将bean名称放到当前线程的缓存中,这里可以看出prototype不支持循环依赖
                    beforePrototypeCreation(beanName);
                    // prototype就是直接调用createBean创建
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    // 跟上面对应
                    afterPrototypeCreation(beanName);
                }
                // 如果是普通 Bean 的话,直接返回 sharedInstance,如果是 FactoryBean 的话,返回它创建的那个实例对象
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            // 11、创建除sington和prototype其他scope的bean
            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

1、将bean名称转换为真正的名称

protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

如果有&前缀,先去除前缀

public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    // 如果不是以&开头的直接返回
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    // 去除&前缀再返回
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

然后如果是别名,将别名转换成真正的名称

public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    do {
        // 每个别名都对应一个名称
        // 可能会有多层别名,所以循环获取直到没有对应关系就是真正的名称
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

2、从单例map中尝试获取bean

public Object getSingleton(String beanName) {
    // true表示允许提前暴露bean
    return getSingleton(beanName, true);
}

/**
 * 解决循环引用逻辑:使用构造函数创建一个 “不完整” 的 bean 实例(之所以说不完整,
 * 是因为此时该 bean 实例还未初始化),并且提前曝光该 bean 实例的 ObjectFactory
 *(提前曝光就是将 ObjectFactory 放到 singletonFactories 缓存),
 * 通过 ObjectFactory 我们可以拿到该 bean 实例的引用,如果出现循环引用,
 * 我们可以通过缓存中的 ObjectFactory 来拿到 bean 实例,从而避免出现循环引用导致的死循环。
 * 这边通过缓存中的 ObjectFactory 拿到的 bean 实例虽然拿到的是 “不完整” 的 bean 实例,
 * 但是由于是单例,所以后续初始化完成后,该 bean 实例的引用地址并不会变,
 * 所以最终我们看到的还是完整 bean 实例
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级单例缓存(即最终存放bean的缓存)中尝试获取bean实例
    Object singletonObject = this.singletonObjects.get(beanName);
    // 1、若一级单例缓存中存在该bean则直接返回
    // 2、不存在则再判断该bean是否正在创建
    //   2.1、没有正在创建,则直接返回null
    //   2.2、正在创建状态则再从二级缓存中判断
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 为什么会返回属性还没注入的bean实例,是为了解决循环依赖
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 从三级缓存中获取到后放到二级缓存中,删除三级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
                    // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

这里之所以使用三级缓存是为了解决循环依赖。

3、从上一步获取到的对象中得到真正的bean,因为有可能是FactoryBean

protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    String currentlyCreatedBean = this.currentlyCreatedBean.get();
    if (currentlyCreatedBean != null) {
        registerDependentBean(beanName, currentlyCreatedBean);
    }

    return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd);
}

调用父类方法获取给定 bean 实例的对象,bean 实例本身或其创建的对象(如果是 FactoryBean)

protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // 如果name是以&为前缀但是beanInstance并不是FactoryBean,则抛异常
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
    }

    // 如果beanInstance不是FactoryBean(普通bean)或者name是以&为前缀(返回FactoryBean本身),
    // 则直接返回beanInstance
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    // 走到下面说明beanInstance是FactoryBean,name不带有&前缀,
    // 需要通过beanInstance创建真正的bean
    
    Object object = null;
    if (mbd == null) {
        // 如果mbd为空,则从缓存中先获取FactoryBean所创建的bean
        object = getCachedObjectForFactoryBean(beanName);
    }
    // 如果是空的就需要从FactoryBean创建
    if (object == null) {
        // Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 如果当前的FactoryBean是单例并且已经在单例缓存中存在
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // 先从factoryBean创建的bean缓存中获取下看存不存在,已经存在就直接返回了
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 如果缓存中不存在则调用doGetObjectFromFactoryBean正在执行从
                // FromFactory创建bean的逻辑
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                // 再次判断下缓存中是否已经存在,如果已经存在了,则使用缓存中的
                // 如果不存在,则需要对object做后置处理和放进缓存
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        // 如果beanName正在创建,则不做后置处理和缓存
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            // 对bean实例进行后置处理,执行所有已注册的BeanPostProcessor的
                            // postProcessAfterInitialization方法
                            
                            // todo Before是在哪里调用的?
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                                            "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    // 将object放进factoryBeanObjectCache缓存,下次直接就从这里面获取
                    if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    // 不是单例,所以每次都需要调用getObject方法创建新的bean
    else {
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

doGetObjectFromFactoryBean,真正去调用getObject创建bean

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
    	throws BeanCreationException {

    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 调用getObject方法
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    // 如果FactoryBean自身还没有完全初始化,则getObject方法不允许返回空对象
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(
                beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        object = new NullBean();
    }
    return object;
}

这里面就只是通过调用FactoryBean的getObject方法来创建bean。

4、针对prototype的bean

判断是否有循环依赖,prototype不能有循环依赖,如果有则抛异常。

5、检查一下这个 BeanDefinition 在容器中是否存在

如果父容器存在并且BeanDefinition在当前容器不存在,则去父容器中查找

6、将bean标记为已创建(或将要创建)

protected void markBeanAsCreated(String beanName) {
    // 双重检查锁确保该bean还没被标记为已创建
    if (!this.alreadyCreated.contains(beanName)) {
        synchronized (this.mergedBeanDefinitions) {
            if (!this.alreadyCreated.contains(beanName)) {
                // Let the bean definition get re-merged now that we're actually creating
                // the bean... just in case some of its metadata changed in the meantime.
                // 清除合并的bean定义,下次需要再重新合并,以防它的一些元数据同时发生变换
                clearMergedBeanDefinition(beanName);
                // 将该beanName添加进已创建名单中
                this.alreadyCreated.add(beanName);
            }
        }
    }
}

protected void clearMergedBeanDefinition(String beanName) {
    this.mergedBeanDefinitions.remove(beanName);
}

7、重新获取合并bean定义并检查

由于在上一步可能清除了beanName的合并bean定义,所以这里重新获取合并bean定义,并检查该合并bean定义。

8、初始化要依赖的Bean

在创建bean之前需要先初始化它要依赖的Bean,这里指的是depends-on 中定义的依赖,不是作为属性的依赖,这里可以看出depends-on可以决定bean的创建顺序。

9、创建单例bean(重点)

根据bean定义的scope属性判断是否单例。
这里就真正开始创建单例,再从单例缓存中获取实例,若没获取到,则从ObjectFactory创建单例bean实例。

// 从单例缓存中获取实例,若没获取到,则从ObjectFactory创建单例bean实例
if (mbd.isSingleton()) {
    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;
        }
    });
    // 如果是普通 Bean 的话,直接返回 sharedInstance,如果是 FactoryBean 的话,返回它创建的那个实例对象
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 用同步块包住,防止并发创建
    synchronized (this.singletonObjects) {
        // 如果已经在singletonObjects存在了,就直接返回
        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 + "'");
            }
            // 将bean名称放进singletonsCurrentlyInCreation缓存中
            // 表示该bean正在创建
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 获取到的是一个完整的并且已经初始化完成的实例
                // 从上面得知getObject方法中就一句代码调用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;
                }
                // 从singletonsCurrentlyInCreation缓存中将bean名称移除,
                // 表示创建完成
                afterSingletonCreation(beanName);
            }
            // 如果是新创建的bean
            if (newSingleton) {
                // 将bean加入到一级单例缓存中,并删除其余两个缓存
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

其中createBean方法在下一篇Spring IOC 源码解析之createBean(四)再解析。

创建完单例后会将bean加入到一级单例缓存中,并删除其余两个缓存

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 添加到单例bean缓存中
        this.singletonObjects.put(beanName, singletonObject);
        // 移除另外两个缓存
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        // 添加到已注册的单例bean缓存中
        this.registeredSingletons.add(beanName);
    }
}

10、创建prototype实例

scope属性为prototype

// 10、创建prototype实例
else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
        // 创建之前先将bean名称放到当前线程的缓存中,这里可以看出prototype不支持循环依赖
        beforePrototypeCreation(beanName);
        // prototype就是直接调用createBean创建
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        // 跟上面对应
        afterPrototypeCreation(beanName);
    }
    // 如果是普通 Bean 的话,直接返回 sharedInstance,如果是 FactoryBean 的话,返回它创建的那个实例对象
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

11、创建除sington和prototype其他scope的bean

// 11、创建除sington和prototype其他scope的bean
else {
    String scopeName = mbd.getScope();
    final Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {
        Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
                return createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
        });
        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    catch (IllegalStateException ex) {
        throw new BeanCreationException(beanName,
                                        "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                        "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                        ex);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值