@Configuration註解 -【Spring底層原理】

1、註解用法

1. 知識背景

  • lite @Bean mode :當@Bean方法在沒有使用@Configuration註解的類中聲明時稱之爲lite @Bean mode
  • Full @Configuration:若是@Bean方法在使用@Configuration註解的類中聲明時稱之爲Full @Configuration

Full @Configuration中的@Bean方法會被CGLIB所代理,而 lite @Bean mode中的@Bean方法不會被CGLIB代理java

2. @Configuration註解做用

  1. 告訴spring這是一個配置類,至關於spring的xml配置文件
  2. 被@Configuration 註解的類,會被cglib代理進行加強
  3. @Configuration類容許經過調用同一類中的其餘@Bean方法來定義bean之間的依賴關係,保證@Bean的對象做用域受到控制,避免多例

2、實例分析

1. 案例

爲了說明@Configuration註解的做用,咱們先來看一個maven建立的spring項目spring

// 啓動類
public class MainTest {
    @Test
    public void TestMain(){
        new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

// 配置類
@Configuration
public class AppConfig {
    @Bean
    public User user(){
        return new User();
    }

    @Bean
    public User2 user2() {
        user();
        return new User2();
    }
}

// 兩個實體類
public class User {
    public User() {
        System.out.println("User對象");
    }
}
public class User2 {
    public User2() {
        System.out.println("User2對象");
    }
}
複製代碼

這是一個最簡單的spring項目,在配置類中有@Configuration註解,咱們運行啓動類,能夠看到以下打印信息:緩存

image-20210115101414931

若是去掉配置類中的@Configuration註解會怎樣呢,去掉以後,我們來看看打印信息:markdown

image-20210115101618212

分析:app

  • 在上面的代碼中,並無直接調用配置類和實體類,說明這些都在spring底層進行了封裝
  • 在配置類中User類是進行了兩次實例化的,但加了@Configuration註解後,只進行一次實例化,說明@Configuration註解將@Bean的方法進行的加強,保證明例爲單實例

2. 問題

問1:@Configuration註解是如何定義bean之間的依賴關係?jvm

問2:@Configuration註解是如何將@Bean方法進行加強的?maven

下面將跟蹤spring源碼來回答這兩個問題ide

3、源碼追蹤

啓動類代碼只有AnnotationConfigApplicationContext類,因此我們以這裏爲入口,點進去能夠看到源碼以下:源碼分析

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    this.register(componentClasses);
    this.refresh();
}
複製代碼

在這段源碼中post

  • this():這個無參構造是和bean相關的
  • register(componentClasses):將componentClasses註冊到beanDefinitionMap集合中去
  • refresh():和@Configuration註解相關

因此我們跟蹤refresh(),點進去查看源碼:

AbstractApplicationContext類中refresh方法】

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        this.prepareRefresh();
        // 告訴子類加載內部bean工廠
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        // 準備在上下文中使用的bean工廠
        this.prepareBeanFactory(beanFactory);

        try {
            // 容許在上下文子類中對bean工廠進行後置處理
            this.postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // 這個方法是源碼分析裏面比較重要的一個入口,這裏只講和@Configuration註解相關的
            // 解析@Configuration配置類,將自定義的BeanFactoryPostProcessor、BeanPostProcessor註冊到beanDefinitionMap
            this.invokeBeanFactoryPostProcessors(beanFactory);
            // 將BeanPostProcessor實例化成bean並註冊到beanFactory的beanPostProcessors
            this.registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            // 註冊國際化相關的Bean
            this.initMessageSource();
            // 爲上下文註冊應用事件廣播器(用於ApplicationEvent的廣播),若是有自定義則使用自定義的,若是沒有則內部實例化一個
            this.initApplicationEventMulticaster();
            this.onRefresh();
            // 註冊全部(靜態、動態)的listener,並廣播earlyApplicationEvents
            this.registerListeners();
            // 實例化用戶自定義的普通單例Bean
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var10) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
            }

            // 銷燬已經建立的單例,以免懸浮資源
            this.destroyBeans();
            this.cancelRefresh(var10);
            throw var10;
        } finally {
            // 在Spring的核心中重置公共緩存
            this.resetCommonCaches();
            contextRefresh.end();
        }

    }
}
複製代碼

上面的源碼給出了部分註釋,咱們能夠看到,和@Configuration註解相關的是invokeBeanFactoryPostProcessors方法,我們繼續跟蹤,點進去看源碼:

AbstractApplicationContext類中invokeBeanFactoryPostProcessors方法】

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // getBeanFactoryPostProcessors(): 拿到當前應用上下文beanFactoryPostProcessors變量中的值
    // invokeBeanFactoryPostProcessors: 實例化並調用全部已註冊的BeanFactoryPostProcessor
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
    if (!IN_NATIVE_IMAGE && beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}
複製代碼

繼續跟蹤invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors()),進入源碼查看:

PostProcessorRegistrationDelegateinvokeBeanFactoryPostProcessors方法】

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    Set<String> processedBeans = new HashSet();
    ArrayList regularPostProcessors;
    ArrayList registryProcessors;
    int var9;
    ArrayList currentRegistryProcessors;
    String[] postProcessorNames;
    // 判斷beanFactory是否爲BeanDefinitionRegistry,beanFactory爲DefaultListableBeanFactory
    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;
        // 用於存放普通的BeanFactoryPostProcessor
        regularPostProcessors = new ArrayList();
        // 用於存放BeanDefinitionRegistryPostProcessor
        registryProcessors = new ArrayList();
        Iterator var6 = beanFactoryPostProcessors.iterator();

        // 遍歷全部的beanFactoryPostProcessors, 將BeanDefinitionRegistryPostProcessor和普通BeanFactoryPostProcessor區分開
        while(var6.hasNext()) {
            BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next();
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor)postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            } else {
                regularPostProcessors.add(postProcessor);
            }
        }

        currentRegistryProcessors = new ArrayList();
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        String[] var16 = postProcessorNames;
        var9 = postProcessorNames.length;

        int var10;
        String ppName;
        // 遍歷postProcessorNames
        for(var10 = 0; var10 < var9; ++var10) {
            ppName = var16[var10];
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f3ce0aeae8f4176b96e2dab04475899~tplv-k3u1fbpfcp-watermark.image)

        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        // 解析配置類,爲配置中的bean定義生成對應beanDefinition,並注入到registry的beanDefinitionMap
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
        currentRegistryProcessors.clear();
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        var16 = postProcessorNames;
        var9 = postProcessorNames.length;

        for(var10 = 0; var10 < var9; ++var10) {
            ppName = var16[var10];
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }

        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
        currentRegistryProcessors.clear();
        boolean reiterate = true;

        while(reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            String[] var19 = postProcessorNames;
            var10 = postProcessorNames.length;

            for(int var26 = 0; var26 < var10; ++var26) {
                String ppName = var19[var26];
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }

            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
            currentRegistryProcessors.clear();
        }

        // 調用ConfigurationClassPostProcessor#postProcessBeanFactory加強配置類
        // 經過cglib生成加強類,加載到jvm內存
        // 設置beanDefinition的beanClass爲加強類,讓@Bean生成的bean是單例
        invokeBeanFactoryPostProcessors((Collection)registryProcessors, (ConfigurableListableBeanFactory)beanFactory);
        invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
    } else {
        invokeBeanFactoryPostProcessors((Collection)beanFactoryPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
    }

    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    regularPostProcessors = new ArrayList();
    registryProcessors = new ArrayList();
    currentRegistryProcessors = new ArrayList();
    postProcessorNames = postProcessorNames;
    int var20 = postProcessorNames.length;

    String ppName;
    for(var9 = 0; var9 < var20; ++var9) {
        ppName = postProcessorNames[var9];
        if (!processedBeans.contains(ppName)) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                regularPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
            } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                registryProcessors.add(ppName);
            } else {
                currentRegistryProcessors.add(ppName);
            }
        }
    }

    sortPostProcessors(regularPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList(registryProcessors.size());
    Iterator var21 = registryProcessors.iterator();

    while(var21.hasNext()) {
        String postProcessorName = (String)var21.next();
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }

    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors((Collection)orderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList(currentRegistryProcessors.size());
    Iterator var24 = currentRegistryProcessors.iterator();

    while(var24.hasNext()) {
        ppName = (String)var24.next();
        nonOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    }

    invokeBeanFactoryPostProcessors((Collection)nonOrderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
    beanFactory.clearMetadataCache();
}
複製代碼

這裏和@Configuration註解相關的方法是invokeBeanFactoryPostProcessors((Collection)registryProcessors, (ConfigurableListableBeanFactory)beanFactory);,繼續跟蹤,進入源碼查看:

PostProcessorRegistrationDelegateinvokeBeanFactoryPostProcessors方法】

private static void invokeBeanFactoryPostProcessors(Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    Iterator var2 = postProcessors.iterator();

    while(var2.hasNext()) {
        BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var2.next();
        StartupStep var10000 = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process");
        postProcessor.getClass();
        StartupStep postProcessBeanFactory = var10000.tag("postProcessor", postProcessor::toString);
        // 調用ConfigurationClassPostProcessor#postProcessBeanFactory加強配置類
        postProcessor.postProcessBeanFactory(beanFactory);
        postProcessBeanFactory.end();
    }
}
複製代碼

繼續跟蹤postProcessor.postProcessBeanFactory(beanFactory);,進入源碼查看,發現是一個接口,我們找到和@Configuration相關的,也就是截圖中框出來的 ,查看源碼:

image-20210115154241215

ConfigurationClassPostProcessor類中postProcessBeanFactory方法】

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    int factoryId = System.identityHashCode(beanFactory);
    if (this.factoriesPostProcessed.contains(factoryId)) {
        throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);
    } else {
        this.factoriesPostProcessed.add(factoryId);
        if (!this.registriesPostProcessed.contains(factoryId)) {
            this.processConfigBeanDefinitions((BeanDefinitionRegistry)beanFactory);
        }

        // 進行代理,爲@Configuration註解的類生成加強類
        this.enhanceConfigurationClasses(beanFactory);
        beanFactory.addBeanPostProcessor(new ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor(beanFactory));
    }
}
複製代碼

該方法會去判斷咱們的bean工廠當中是否有bean須要進行cglib代理,並在enhanceConfigurationClasses(beanFactory)方法中進行代理,爲@Configuration註解的類生成加強類,繼續跟蹤,點進enhanceConfigurationClasses,以下:

ConfigurationClassPostProcessor類中enhanceConfigurationClasses方法】

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap();
    String[] var4 = beanFactory.getBeanDefinitionNames();
    int var5 = var4.length;

    for(int var6 = 0; var6 < var5; ++var6) {
        String beanName = var4[var6];
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        // 在ConfigurationClassUtils中標記是Full @Configuration仍是lite @Bean mode
        Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
        MethodMetadata methodMetadata = null;
        if (beanDef instanceof AnnotatedBeanDefinition) {
            methodMetadata = ((AnnotatedBeanDefinition)beanDef).getFactoryMethodMetadata();
        }

        if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
            AbstractBeanDefinition abd = (AbstractBeanDefinition)beanDef;
            if (!abd.hasBeanClass()) {
                try {
                    abd.resolveBeanClass(this.beanClassLoader);
                } catch (Throwable var13) {
                    throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), var13);
                }
            }
        }

        // 1.判斷是不是全類註解
        if ("full".equals(configClassAttr)) {
            if (!(beanDef instanceof AbstractBeanDefinition)) {
                throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
            }

            if (this.logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
                this.logger.info("Cannot enhance @Configuration bean definition '" + beanName + "' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.");
            }

            // 2.是全註解則將beandefinition放入map中
            configBeanDefs.put(beanName, (AbstractBeanDefinition)beanDef);
        }
    }

    if (!configBeanDefs.isEmpty() && !IN_NATIVE_IMAGE) {
        ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
        Iterator var15 = configBeanDefs.entrySet().iterator();

        // 3.而後遍歷這個map
        while(var15.hasNext()) {
            Entry<String, AbstractBeanDefinition> entry = (Entry)var15.next();
            AbstractBeanDefinition beanDef = (AbstractBeanDefinition)entry.getValue();
            beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            Class<?> configClass = beanDef.getBeanClass();
            // 4.進行cglib代理,爲@Configuration註解的類生成加強類
            Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
            if (configClass != enhancedClass) {
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
                }

                // 再經過beanDef.setBeanClass(enhancedClass)修改beanDefinition的BeanClass屬性,
                // 在bean實例化階段,會利用反射技術將beanClass屬性對應的類實例化出來
                // 因此最終實例化出來的@Configuration bean是一個代理類的實例
                beanDef.setBeanClass(enhancedClass);
            }
        }

        enhanceConfigClasses.tag("classCount", () -> {
            return String.valueOf(configBeanDefs.keySet().size());
        }).end();
    } else {
        enhanceConfigClasses.end();
    }
}
複製代碼

ConfigurationClassUtils類中聲明瞭是Full @Configuration仍是lite @Bean mode,能夠看看源碼:

ConfigurationClassUtils類中checkConfigurationClassCandidate方法】

Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 若是存在@Configuration註解,則爲BeanDefinition設置configurationClass屬性爲full
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "full");
} else {
    if (config == null && !isConfigurationCandidate(metadata)) {
        return false;
    }

    // 若是沒有@Configuration註解或者有Component,ComponentScan,Import,ImportResource 註解中的任意一個,
    // 或者存在 被@bean 註解的方法,則返回true.
    // 則設置configurationClass屬性爲lite
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, "lite");
}

Integer order = getOrder(metadata);
if (order != null) {
    beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
複製代碼

跟蹤到這裏,基本上就比較明朗了,能夠獲得咱們想要的答案

  1. ConfigurationClassUtils類中的checkConfigurationClassCandidate標記是Full @Configuration仍是lite @Bean mode
  2. 經過"full".equals(configClassAttr)判斷是不是全類註解
  3. 是全註解則將beandefinition放入map中configBeanDefs.put
  4. 遍歷這個map
  5. 使用cglib技術爲配置類生成一個enhancedClass
  6. 經過enhancer.enhance進行cglib代理,爲@Configuration註解的類生成加強類
  7. 再經過beanDef.setBeanClass(enhancedClass)修改beanDefinition的BeanClass屬性,在bean實例化階段,會利用反射技術將beanClass屬性對應的類實例化出來,因此最終實例化出來的@Configuration bean是一個代理類的實例

使用了@Configuration註解的類,屬於Full @Configuration。@Configuration類容許經過調用同一類中的其餘@Bean方法來定義bean之間的依賴關係,保證@Bean的對象做用域受到控制,避免多例。@Configuration類中的@Bean地方會被CGLIB進行代理。Spring會攔截該方法的執行,在默認單例狀況下,容器中只有一個Bean,因此咱們屢次調用user()方法,獲取的都是同一個對象。

對於@Configuration註解的類中@Bean標記的方法,返回的都是一個bean,在加強的方法中,Spring會先去容器中查看一下是否有這個bean的實例了,若是有了的話,就返回已有對象,沒有的話就建立一個,而後放到容器中。

是如何進行代理的,我們還能夠繼續跟蹤:點開enhancer.enhance,進入ConfigurationClassEnhancer類,在這個類中會去執行cglib代理類中的代理方法,以下:

ConfigurationClassEnhancer類中static靜態方法】

static {
    CALLBACKS = new Callback[]{new ConfigurationClassEnhancer.BeanMethodInterceptor(), 
                               new ConfigurationClassEnhancer.BeanFactoryAwareMethodInterceptor(), NoOp.INSTANCE};
    CALLBACK_FILTER = new ConfigurationClassEnhancer.ConditionalCallbackFilter(CALLBACKS);
    logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
    objenesis = new SpringObjenesis();
}
複製代碼

cglib代理主要就是callBacks中的方法,點進去能夠看到是一個接口,其中有一個實現類就是和cglib相關的

image-20210115201141656

4、總結

@Configuration註解底層是如何實現的,經過源碼我們能夠反推並總結爲如下幾點:

  1. Spring首先會獲取到全部的beandefenition
  2. ConfigurationClassUtils類中checkConfigurationClassCandidate方法判斷是Full @Configuration仍是lite @Bean mode
  3. 經過ConfigurationClassPostProcessor後置處理器遍歷全部的beandefenition
  4. 將標記了Full @Configuration模式的beandefenition,會對這個類進行cglib代理,生成一個代理類,並把這個類設置到BeanDefenition的Class屬性中
    1. 配置類會被CGLIB加強(生成代理對象),放進IoC容器內的是代理
    2. 對於內部類是沒有限制的:能夠是Full模式或者Lite模式
    3. 配置類內部能夠經過方法調用來處理依賴,而且可以保證是同一個實例,都指向IoC內的那個單例
  5. 須要用這個Bean實例的時候,從這個Class屬性中拿到的Class對象進行反射,最終反射出來的是代理加強後的類
  6. 經過@Configuration標註類的Bean,Spring會先去容器中查看是否有這個Bean實例,若是有就返回已有的對象,沒有就建立一個,而後放到容器中
相關文章
相關標籤/搜索