問:Spring容器、SpringIOC是什么?
那么Spring容器到底是什么東西呢,長什么樣子呢?為什么感覺像是哆啦A夢的百寶箱,什么東西都能拿出來?
所以本文針對以上問題對一個簡單的說明,
1、首先我們整個過程中要保持一個概念,容器中的Bean要經歷以下步驟帶着這個思想去看就會好理解一些:
2、來個簡單的例子
🌰:XML方式定義Bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="date" class="java.util.Date"/> </beans>
上面 xml 描述了一個日期Date對象的定義
🌰:簡單使用
public class Application { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Date date = context.getBean("date", Date.class); System.out.println(Objects.requireNonNull(date).getTime()); } }
上述代碼做了三件事:1.初始化容器,將Bean定義等信息加載進去;2.從容器中獲取剛剛定義的Bean;3.驗證Bean是否已經真的可以正常使用
主要說一下 前兩件事情,對於Spring來說都做了那些事情
3、So,開始擼代碼
🌰:根據上面介紹說到的總體思想,我們bean的定義這一步已經做好了,現在就是等待告訴Spring 咱bean定義文件叫什么名,在哪里。代碼中的第一行既是此操作。
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent);
//設置文件位置 this.setConfigLocations(configLocations); if (refresh) { this.refresh(); } }
Spring知道咱的定義位置之后該做什么呢?那肯定是要獲取並加載這個資源了。Spring中資源文件的加載獲取會實現接口 ResourceLoader,這個接口一共兩方法。獲取資源And獲取ClassLoader
public interface ResourceLoader { /** Pseudo URL prefix for loading from the class path: "classpath:" */ String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; /**Return a Resource handle for the specified resource location. */ Resource getResource(String location); ClassLoader getClassLoader(); }
題外話:查看該接口的實現,可以很清楚的看到我們常用的一些Context的資源加載方式,
接着上面說Spring是在何時去使用這個資源加載的呢?Spring在 AbstractApplicationContext 這個抽象類中定義了容器初始化的模版方法
public void refresh() throws BeansException, IllegalStateException { synchronized(this.startupShutdownMonitor) { this.prepareRefresh();//准備工作 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//創建容器 this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
上述模版方法中包含整個容器初始化所做的很多工作,篇幅有限,只對創建容器以及Bean的初始化做說明,其他bean前、bean后處理、事件廣播等等有興趣自己可以看下源碼。
這個模版方法里其實根據方法名都已經能夠很清楚的知道在做些什么事情。首先一上來就開始做容器初始化的准備工作,然后接下來是真正的創建容器。也就是在這個時候將資源文件加載並且解析成為Spring中自己定義的Bean的數據結構
protected final void refreshBeanFactory() throws BeansException { //如果存在進行銷毀 if (this.hasBeanFactory()) { this.destroyBeans(); this.closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = this.createBeanFactory();//創建bean工程 beanFactory.setSerializationId(this.getId()); this.customizeBeanFactory(beanFactory); this.loadBeanDefinitions(beanFactory);//加載Bean的定義 synchronized(this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException var5) { throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5); } }
上述代碼中關鍵點1創建的實際上是一個 DefaultListableBeanFactory 實例,該實例包含了Spring IOC最基礎的功能。
所以至此我們就可以回答第一個問題了,Spring容器長什么樣?以下代碼是 DefaultListableBeanFactory 中的部分屬性定義。基於此我們可以大概明白日常使用中根據bean name或者bean type來獲取bean是怎么一回事了。其實說白了就是從一個事先准備好的Map里面找到對應key的value值返回(當然實際上還有更多的操作,比如緩存、lazy等等),
/** Optional OrderComparator for dependency Lists and arrays */ private Comparator<Object> dependencyComparator; /** Resolver to use for checking if a bean definition is an autowire candidate */ private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver(); /** Map from dependency type to corresponding autowired value */ private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<Class<?>, Object>(16); /** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256); /** Map of singleton and non-singleton bean names, keyed by dependency type */ private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64); /** Map of singleton-only bean names, keyed by dependency type */ private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64); /** List of bean definition names, in registration order */ private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);
對於 loadBeanDefinitions 這個方法,其作用是加載 beanDefinition。BeanDefinitions接口實際上是描述了bean定義過程中的操作。實際上Bean定義時定義了那些東西。這個可以在其實現類中找到,其部分屬性如下(有興趣可以針對每個屬性做詳細理解):
private volatile Object beanClass; private String scope = SCOPE_DEFAULT; private boolean abstractFlag = false; private boolean lazyInit = false; private int autowireMode = AUTOWIRE_NO; private int dependencyCheck = DEPENDENCY_CHECK_NONE; private String[] dependsOn; private boolean autowireCandidate = true; private boolean primary = false; private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<String, AutowireCandidateQualifier>(0); private boolean nonPublicAccessAllowed = true; private boolean lenientConstructorResolution = true; private String factoryBeanName; private String factoryMethodName; private ConstructorArgumentValues constructorArgumentValues; private MutablePropertyValues propertyValues; private MethodOverrides methodOverrides = new MethodOverrides(); private String initMethodName; private String destroyMethodName; private boolean enforceInitMethod = true; private boolean enforceDestroyMethod = true; private boolean synthetic = false; private int role = BeanDefinition.ROLE_APPLICATION; private String description; private Resource resource;
從上面的屬性可以看到Bean定義過程中包含:bean是否懶加載、是否抽象、自動注入模式、依賴關系、是否Primary Bean、初始化方法、銷毀方法等等。
loadBeanDefinitions 的作用就是將咱的XML轉化為BeanDefinitions然后放入上述DefaultListableBeanFactory實例的 beanDefinitionMap 中。
具體過程:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = this.getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = this.getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
Resource進行包裝編碼,使用前文提到的ResourceLoader中的getResource方法拿到資源轉為輸入流
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
接下來關鍵的地方來了,doLoadBeanDefinitions 方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try {
//這里我就不解釋了,總體意思就是將利用將DOM元素轉化為BeanDefinitions ,有興趣的化可以看下XML文檔在整個過程中的校驗、解析等操作,以及在register過程(DefaultBeanDefinitionsDocumentReader類)中使用到的代理技術。 Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
至此,定義、解析容器創建已完成
接下來是不是該將定義的BeanDefinitions轉化為真正可用的Bean了呢?是的,回到一開始的模版方法refresh(),找到 finishBeanFactoryInitialization 方法。然后一探究竟:
同樣的抽象類 AbstractApplicationContext 已經給實現了bean。(所以說面向接口編程很重要呢)
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { if (beanFactory.containsBean("conversionService") && beanFactory.isTypeMatch("conversionService", ConversionService.class)) { beanFactory.setConversionService((ConversionService)beanFactory.getBean("conversionService", ConversionService.class)); } if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { public String resolveStringValue(String strVal) { return AbstractApplicationContext.this.getEnvironment().resolvePlaceholders(strVal); } }); } String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); String[] var3 = weaverAwareNames; int var4 = weaverAwareNames.length; for(int var5 = 0; var5 < var4; ++var5) { String weaverAwareName = var3[var5]; this.getBean(weaverAwareName); } beanFactory.setTempClassLoader((ClassLoader)null); beanFactory.freezeConfiguration(); beanFactory.preInstantiateSingletons(); }
/**
* 首先這個方法來自上文已經提到過的 DefaultListableBeanFactory ,所以 beanDefinitionNames 當然是在初始化容器的時候放進去的啦。*/
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("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<String>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) {
//說明一點:Spring 中是有父子容器的,所以在查看源碼時候,你會看到很多有關parent的東西 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//不是抽象的 是單例 不是懶加載的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) {//實際上就是判斷這個bean名字是不是以指定前綴開始的,默認“&” final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; } }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
上面的代碼官方對於兩個for循環,已經做出了很具體的說明,像我這種英語渣渣都能看懂,相信你也能看懂。我們主要說明第一個for循環中做的事情。至於初始化完成之后的回調操作這里就不做解析了。
接下來,主要看下getBean方法,由於此函數內容較多,就不直接展示了。getBean方法一上來就會先到cache中去尋找該Bean,找到直接返回。找不到會先在父容器中尋找,找到返回,找不到才會執行實例化邏輯。

protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } 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. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. 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. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { 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 = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } 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); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } 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, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { 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; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && bean != null && !requiredType.isInstance(bean)) { try { return getTypeConverter().convertIfNecessary(bean, requiredType); } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
先說明以下在初始化Bean之前,當前這個Bean如果需要注入其他屬性,Spring是怎么實現的:
// Guarantee initialization of beans that the current bean depends on. 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); } } }
首先遍歷依賴的bean名稱,進行依賴注冊。接着真正加載依賴Bean。這樣就構成了一個遞歸循環,出口自然是被依賴的bean沒有任何依賴。
接下來是Bean的真正實例化。由於Bean的Scope不盡相同,這里只做單例的說明:
// Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
//真正實例化要執行的操作,會被getSingleton調用 @Override public Object getObject() throws BeansException { 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 = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. ..... } else { ....... }
getSingleton方法會去執行實例化操作,並且將實例注冊進容器,也就是Map;
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { .....try {
//實例化,並改變標記為true 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 != NULL_OBJECT ? singletonObject : null); } }
真正實例化的操作方法 createBean,其中包括整個Bean的屬性注入以及方法校驗。后續如果有機會再擼下這塊的具體注入過程。
最后一步,使用Bean。看完前面的內容,其實這一步說與不說是一樣的,因為
Date date = context.getBean("date", Date.class);
這句話實際上調用就是上文中的getBean方法。getBean方法一上來就會先到cache中去尋找該Bean,找到直接返回。找不到會先在父容器中尋找,找到返回,找不到才會自己執行上述邏輯。
總結:
Spring容器簡單來說就是一個個Map。維護着各種關系,比如,名稱跟Bean定義、名稱、別名、類型等之間的相互關系。在容器啟動時候,初始化完成各種事件的注冊以及部分bean的實例化。使用到cache(實際上也是Map)、父子容器這種邏輯關系,使得容器更加可用。當然本文只說明了的加載配置文件方式Bean初始化的過程。實際上其他Bean定義方式也是一樣的,只不過是BeanDefinitions的來源不一樣。
感概:
作為一個開發者來說,數據結構跟設計模式真的特別重要。兩者恰到好處的結合才能寫出優雅的代碼。不為了使用而使用,強搬設計模式或者數據結構,這樣的代碼反而使可讀性下降、程序性能也隨之降低。比如說單例模式,很多人可能對這個都熟到不能再熟。許多人都寫到最安全最方便的該是使用枚舉來實現單例模式。既解決了線程安全問題,也解決了反射帶來的多實例問題。事實上在Spring中也有使用到單例模式,但是其代碼卻是使用 雙重判斷 的單例。所以適合自己的才是最好的。