Spring 源碼學習筆記(一)對象創建流程梳理

脈絡思路:學習Spring的兩大特性IOC與AOP,都離不開容器,那麼就要先了解容器是什麼?如何使用?容器是對象從創建到使用,再到銷燬整個流程的控制。下面梳理下創建對象過程,加載配置文件 ——> 解析——> 封裝BeanDefinition對象 ——> 實例化 ——> 完整對象。。。

下面從加載配置文件入手:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

<context:property-placeholder location="classpath:org/springframework/web/context/WEB-INF/myplaceholder.properties"/>

  <bean id="myBean" class="org.springframework.beans.factory.access.TestBean">
	  <property name="name" value="${myBeanValue}"></property>
  </bean>

</beans>
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.access.TestBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SimpleDemoTest {

	@Test
	public void test(){
		ApplicationContext context = new ClassPathXmlApplicationContext("org/springframework/web/context/beans1.xml");
		TestBean bean = (TestBean) context.getBean("mybean");
		System.out.println(bean.getName());
	}
}

1、加載配置文件,封裝成beanDefinition對象

爲了可以擴展加載更多類型的配置文件,提供一層BeanDefinitionReader接口層(讓不同類型的加載Reader去實現)。加載XML配置文件,實際上用XmlBeanDefinitionReader讀取配置文件,統一封裝成BeanDefinition對象。

在啓動過程是一個核心方法refresh(),refresh()方法實現是在AbstractApplicationContext.java中

/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

下面對refresh()中各個方法做個簡單的介紹:

@Override
public void refresh() throws BeansException, IllegalStateException {
    //startupShutdownMonitor對象在spring環境刷新和銷燬的時候都會用到,確保刷新和銷燬不會同時執行
    synchronized (this.startupShutdownMonitor) {
        // 準備工作,例如記錄事件,設置標誌,檢查環境變量等,並有留給子類擴展的位置,用來將屬性加入到applicationContext中
        prepareRefresh();

        // 創建beanFactory,這個對象作爲applicationContext的成員變量,可以被applicationContext拿來用,
        // 並且解析資源(例如xml文件),取得bean的定義,放在beanFactory中
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 對beanFactory做一些設置,例如類加載器、spel解析器、指定bean的某些類型的成員變量對應某些對象等
        prepareBeanFactory(beanFactory);

        try {
            // 子類擴展用,可以設置bean的後置處理器(bean在實例化之後這些後置處理器會執行)
            postProcessBeanFactory(beanFactory);

            // 執行beanFactory後置處理器(有別於bean後置處理器處理bean實例,beanFactory後置處理器處理bean定義)
            invokeBeanFactoryPostProcessors(beanFactory);

            // 將所有的bean的後置處理器排好序,但不會馬上用,bean實例化之後會用到
            registerBeanPostProcessors(beanFactory);

            // 初始化國際化服務
            initMessageSource();

            // 創建事件廣播器
            initApplicationEventMulticaster();

            // 空方法,留給子類自己實現的,在實例化bean之前做一些ApplicationContext相關的操作
            onRefresh();

            // 註冊一部分特殊的事件監聽器,剩下的只是準備好名字,留待bean實例化完成後再註冊
            registerListeners();

            // 單例模式的bean的實例化、成員變量注入、初始化等工作都在此完成
            finishBeanFactoryInitialization(beanFactory);

            // applicationContext刷新完成後的處理,例如生命週期監聽器的回調,廣播通知等
            finishRefresh();
        }

        catch (BeansException ex) {
            logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

            // 刷新失敗後的處理,主要是將一些保存環境信息的集合做清理
            destroyBeans();

            // applicationContext是否已經激活的標誌,設置爲false
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }
    }
}

跟進方法obtainFreshBeanFactory(),可以看到核心方法AbstractRefreshableApplicationContext.java中refreshBeanFactory(),創建beanFactory,同時加載全部beanDefinition對象,此時只做簡單加載(其中配置佔位符不會被替換,替換是在beanFactoryPostProcessor中完成)

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
            //解析資源(例如xml文件),取得bean的定義,放在beanFactory中
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

 

2、創建beanFactory,執行後置處理器

BeanDefinition對象——>實例化,這個過程不只是new/invoke,還做了哪些操作?

BeanFactory(Interface)Spring的本質是一個bean工廠(beanFactory)或者bean容器。解決bean之間的依賴問題,達到了松耦合的效果。 在沒有spring這個beanFactory之前,我們都是直接通過new來實例化各種對象,現在各種對象bean的生產都是通過beanFactory來實例化的,這樣的話,spring這個beanFactory就可以在實例化bean的各個階段進行一些額外的處理。在bean的生命週期的各個階段對bean進行管理,並且spring將這些階段通過各種接口暴露給我們。 我們只要讓bean實現對應的接口,那麼spring就會在bean的生命週期調用我們實現的接口來處理該bean

BeanFactoryPostProcessor(Interface): BeanFactory後置處理器,是對BeanDefinition對象進行修改。

BeanPostProcessor(Interface):Bean後置處理器,是對生成的Bean對象進行修改。 (初始化中使用)

後置處理器也被抽象出來,通過實現接口類BeanFactoryPostProcessor就可以自定義創建處理器。下面加入一個MyPostProcessor

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

<context:property-placeholder location="classpath:org/springframework/web/context/WEB-INF/myplaceholder.properties"/>

  <bean id="myPostProcessor" class="org.springframework.MyPostProcessor"/>

  <bean id="myBean" class="org.springframework.beans.factory.access.TestBean">
	  <property name="name" value="${myBeanValue}"></property>
  </bean>

</beans>
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("==> come to myPostProcessor ...");
	}
}
控制檯打印:
---------------------------------
==> come to myPostProcessor ...
this is a test
BUILD SUCCESSFUL in 32s

註解是繼配置文件之後的擴展,也是在beanFactory後置處理器位置中做了處理。(同樣放在後置處理器的還有,Springboot中註解自動裝配功能實現方法,跟進ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法。可以仿照@autowire註解處理器AutowiredAnnotationBeanPostProcessor添加自定義註解,完成註解自動裝配需求的實現)

/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);

		processConfigBeanDefinitions(registry);
	}


	/**
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        ...
        // Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();
                        ...
                }
        }
class ConfigurationClassParser {
        protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
        ...
	      processConfigurationClass(new ConfigurationClass(metadata, beanName));
        ...
        }


        protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        ...
        do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
        ...
        }

        protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}


		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}
                return null;

        }

}

 

3、對象實例化

猜測一下,通過反射的方式進行實例化代碼如下(在堆空間中開啓空間,屬性都是默認的)

Constructor ctor = clazz.getDeclareConsturctor();
Object obj = ctor.newinstance();

下面開始驗證,跟進下refresh()中 finishBeanFactoryInitialization(beanFactory)

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}

beanFactory.preInstantiateSingletons()默認創建單例的對象,繼續跟進。

@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}

		...
	}

去查找beanName,跟進getBean(beanName)——>doGetBean(name)——>createBean(beanName, mbd,  args)——>doCreateBean(beanName, mdbToUse, args)——>createBeanInstance(beanName, mbd, args)可以看到有構造器的創建——>instantiateBean(beanName, mbd)方法——>.instantiate(mbd, beanName, parent)有在創建構造器,那麼期待接下來創建實例new instance()——>BeanUtils.instantiateClass(constructorToUse)果然有new instance

Constructor<?> constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
   Assert.notNull(ctor, "Constructor must not be null");
   try {
      ReflectionUtils.makeAccessible(ctor);
      if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
         return KotlinDelegate.instantiateClass(ctor, args);
      }
      else {
         Class<?>[] parameterTypes = ctor.getParameterTypes();
         Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
         Object[] argsWithDefaultValues = new Object[args.length];
         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);
      }
   }
   catch (InstantiationException ex) {
      throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
   }
   catch (IllegalAccessException ex) {
      throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
   }
   catch (IllegalArgumentException ex) {
      throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
   }
   catch (InvocationTargetException ex) {
      throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
   }
}

這就是實例化的老巢了...

 

4、對象的初始化

spring中一般分爲自定義bean對象和容器容器對象,當在自定義對象中調用容器對象,可以通過aware接口去代替get/set方法。

/**
 * Interface to be implemented by beans that wish to be aware of their
 * owning {@link BeanFactory}.
 */
public interface BeanFactoryAware extends Aware {

	void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}
/**
 * Interface to be implemented by any object that wishes to be notified
 * of the {@link ApplicationContext} that it runs in.
 */
public interface ApplicationContextAware extends Aware {

	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

before、after對bean進行擴展beanPostProcessor,例如:AOP動態代理有cglib和jdk兩種方式,下面跟進一下BeanPostProcessor接口

public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

可以找一下動態代理的實現類AbstractAutoProxyCreator,before方法直接返回bean,after方法中跟進wrapIfNecessary

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		return bean;
	}

        @Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return 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;
		}

		// Create proxy if we have advice.
		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;
	}

        /**
	 * Create an AOP proxy for the given bean.
	 * @param beanClass the class of the bean
	 * @param beanName the name of the bean
	 * @param specificInterceptors the set of interceptors that is
	 * specific to this bean (may be empty, but not null)
	 * @param targetSource the TargetSource for the proxy,
	 * already pre-configured to access the bean
	 * @return the AOP proxy for the bean
	 * @see #buildAdvisors
	 */
	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(getProxyClassLoader());
	}

init方法,執行配置文件中init-method方法

下面來找一個初始化的老巢,同之前的實例化refresh()——>finishBeanFactoryInitialization(beanFactory)——>getBean(beanName)——>doGetBean(name)——>createBean(beanName, mbd,  args)——>doCreateBean(beanName, mdbToUse, args),在這個方法裏,createBeanInstance(beanName, mbd, args)之後返回實例化對象,然後執行——>populateBean(beanName, mbd, instanceWrapper)填充屬性和initializeBean(beanName, exposedObject, mbd)調用aware方法初始化

====================================================

梳理下容器對象的創建流程:(搞懂Refresh()裏面的13個方法,後面將會細化學習)

  1. 創建容器 —— prepareRefresh()
  2. 加載配置文件,封裝成 BeanDefinition —— obtainFreshBeanFactory()
  3. 調用 BeanFactoryPostProcessor —— invokeBeanFactoryPostProcessors(beanFactory)
  4. 準備工作BeanPostProcessor —— registerBeanPostProcessors(beanFactory)
  5. 準備工作② 監聽器、事件、廣播器等 —— initApplicationEventMulticaster()、registerListeners()
  6. 實例化 —— finishBeanFactoryInitialization(beanFactory)
  7. 初始化—— finishBeanFactoryInitialization(beanFactory)
  8. 獲取完整對象 —— finishBeanFactoryInitialization(beanFactory)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章