摘要:在有了之后,類與類之間的耦合關系是這樣的。為刷新做好準備。初始化上下文消息為這個上下文初始化事件多播器。
what is IOC?
IOC(Inversion of Control )控制反轉,是Spring框架最重要的一個特性,提供了一個裝載類的容器來為類之間進行解耦,并提供了一系列的擴展接口,使得開發者可以在bean的生命周期里自定義一些行為操作。
在沒有IOC之前,類與類之間的耦合關系是這樣的。這兒僅僅只有5個類,類之間的耦合關系就如此復雜,難以想象當類的數量隨著業務發展而爆炸增多的時候,耦合關系是多么的糟糕。
在有了IOC之后,類與類之間的耦合關系是這樣的。所有類都注冊在IOC容器上,所有類只和IOC容器耦合,并且IOC容器為所有管理的類提供生命周期的管理。
how IOC do?可以看出來IOC容器很像我們生活中的百貨商場,我們需要什么東西都可以去百貨商場買到,在沒有百貨商場以前我們買菜需要去菜市場,買手機需要去手機店,買電腦需要到電腦城......極大的方便了獲取類的方式。接下來我通過Debug源碼的方式來追一追整個IOC啟動過程的步驟,希望能揭開這個“百貨商場”的神秘面紗。
這是我們debug的入口,用的是最基礎的XML解析的方式。
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); HelloService service = (HelloService) context.getBean("service"); service.say();
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); //設置parent父容器 setConfigLocations(configLocations);//讓ioc容器感知到xml配置文件 if (refresh) { refresh(); } }
做的第一步是super(parent),可以看出IOC容器是具備父子關系的,順便提一下,這個特性在SpringMVC中體現出來了,SpringMVC的容器是 Spring容器的子容器,這樣的結果就是Controller(SpringMVC容器中的Bean)可以調用Service(Spring容器中的類),而反過來則不行,這樣一定程度保證了類之間的單向關系,以及調用方式不可逆,使得容器更加安全。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 為刷新做好準備。 prepareRefresh(); // 告訴子類刷新內部bean工廠。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 準備beanfactory,為使用ioc容器做好準備 prepareBeanFactory(beanFactory); try { // 允許beanfactory準備好后做一些事情,擴展點 postProcessBeanFactory(beanFactory); // 將bean注冊在ioc容器 invokeBeanFactoryPostProcessors(beanFactory); // 注冊攔截bean創建的bean處理器。 registerBeanPostProcessors(beanFactory); // 初始化上下文消息 initMessageSource(); // 為這個上下文初始化事件多播器。 initApplicationEventMulticaster(); // 初始化特定上下文子類中的其他特殊bean。 onRefresh(); // 檢查listener類并注冊它們 registerListeners(); // 實例化所有的lazy-init單例 finishBeanFactoryInitialization(beanFactory); // 最后一步:發布相應的事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 銷毀已經創建的單例 destroyBeans(); // 重置active的值 cancelRefresh(ex); // 將異常傳播給調用者 throw ex; } finally { //清空一些元數據的內存,這些元數據生成了單例模式后就再也用不到了 resetCommonCaches(); } } }
這里是ioc容器的誕生點 createBeanFactory()
protected final void refreshBeanFactory() throws BeansException { ...... DefaultListableBeanFactory beanFactory = createBeanFactory(); ...... loadBeanDefinitions(beanFactory); ...... }
再看看loadBeanDefinitions()方法 ,這里解釋下什么是BeanDefinition (所有的Bean在Spring容器中的數據結構都是BeanDefinition,其中包含了跟這個bean相關的所有信息)beanDefinitionReader可以看做是一個IOC容器bean的生產者,可以從外部環境(xml,注解等)獲取到bean的信息并裝載進去,這個方法對beanDefinitionReader 設置了ResourceLoader,EnitityResolver等等對解析xml文件很重要的類,繼續Debug看。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context"s // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
ResourceLoader會去解析xml文件,將每個xml中的每個元素都解析然后返回一個DOM的文檔樹便于后續操作。
接著會執行prepareBeanFactory方法,這個方法類似一個制定ioc規則的方法,讓哪些接口的bean不注冊進去,哪些接口的bean注冊進去
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context"s class loader etc. beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register early post-processor for detecting inner beans as ApplicationListeners. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found. if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // Register default environment beans. if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
之后會對實現了BeanFactoryPostProcessor接口的類處理,該擴展點允許在(容器已經初始完成,但是bean還沒有初始化這部分時間進行擴展),體現了Spring的擴展性。
又看到了一個擴展點,BeanPostProcessor,該擴展點允許在bean 初始化之前或者之后的時候進行擴展
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
接下來回去預實例化沒有設置lazy-init的類
beanFactory.preInstantiateSingletons();
進入這個方法體,在debug已經可以看到自己設置的bean的id值了
可以看到除了類之外還有scope,lazyInit,primary等屬性
這兒走了一遍getBean的邏輯,不過因為沒有初始化,去到的實例都是null,接著會標記即將創建的bean,并將其緩存
/** 標記已創建的(或即將創建的)指定bean。 這允許bean工廠優化其緩存重復。 創建指定的bean。 * @param beanName the name of the bean */ protected void markBeanAsCreated(String beanName) { 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. clearMergedBeanDefinition(beanName); this.alreadyCreated.add(beanName); } } } }
馬上進入實例化bean的代碼,其中createBean()是重點
// Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory
追了好幾層終于看到,bean是通過反射實例化的。
Class> resolvedClass = ClassUtils.forName(className, classLoader);
如果是singleton,直接從map里面取,如果沒有則通過反射生成,放進map中然后返回,如果是prototype則每次獲取都會實例化,返回一個新的。
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory
這就是緩存單例bean的map
private final MapsingletonObjects = new ConcurrentHashMap (256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { 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); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }總結
ResourceLoader對xml文檔樹進行讀取解析生成Resource文件,Resource文件封裝了對xml文件的IO操作,然后BeanDefinitionReader會對Resource文件讀取并生成BeanDefinition放在BeanDefinitionRegistry里面,過后beanFactoryPostProcesser會解析占位符,然后SimpleInstantiationStrategy會通過反射實例化,然后BeanWrapper會對非lazy-init的bean進行賦值,在getBean的時候IOC容器通過Map緩存的方式來達到單例的效果。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76673.html
摘要:何為簡單點來定義就是切面,是一種編程范式。定義一個切面的載體定義一個切點定義一個為,并指定對應的切點一個注冊配置類,啟動容器,初始化時期獲取對象,獲取對象時期,并進行打印好了,這樣我們整體的代理就已經完成。 問題:Spring AOP代理中的運行時期,是在初始化時期織入還是獲取對象時期織入? 織入就是代理的過程,指目標對象進行封裝轉換成代理,實現了代理,就可以運用各種代理的場景模式。 ...
摘要:在上文中,我實現了一個很簡單的和容器。比如,我們所熟悉的就是在這里將切面邏輯織入相關中的。初始化的工作算是結束了,此時處于就緒狀態,等待外部程序的調用。其中動態代理只能代理實現了接口的對象,而動態代理則無此限制。 1. 背景 本文承接上文,來繼續說說 IOC 和 AOP 的仿寫。在上文中,我實現了一個很簡單的 IOC 和 AOP 容器。上文實現的 IOC 和 AOP 功能很單一,且 I...
摘要:前言以下源碼基于版本解析。實現源碼分析對于的實現,總結來說就是定位加載和注冊。定位就是需要定位配置文件的位置,加載就是將配置文件加載進內存注冊就是通過解析配置文件注冊。下面我們從其中的一種使用的方式一步一步的分析的實現源碼。 前言 以下源碼基于Spring 5.0.2版本解析。 什么是IOC容器? 容器,顧名思義可以用來容納一切事物。我們平常所說的Spring IOC容器就是一個可以容...
摘要:依賴注入是向某個類或方法注入一個值,其中所用到的原理就是控制反轉。但發現更多時間是在調和的源碼。里面就是從中取出這個,完成控制反轉的。控制反轉的優點最后來以我個人觀點談談控制反轉的優點吧。控制反轉為了降低項目耦合,提高延伸性。 本章開始來學習下Spring的源碼,看看Spring框架最核心、最常用的功能是怎么實現的。網上介紹Spring,說源碼的文章,大多數都是生搬硬推,都是直接看來的...
摘要:啟動原理和執行原理分析一的啟動原理我們打開,注意看下面兩個依賴我們第一步是繼承了父項目,然后在添加啟動器的依賴,項目就會自動給我們導入關于項目所需要的配置和。 上一篇我們看到,我們很輕松的完成了項目的構建,那么SpringBoot是如何做到的呢,在使用的使用又有哪些通用配置和注意事項呢? 其實SpringBoot給我們做了大量的自動配置功能,我們只需要引入對應的啟動器就可以直接使用,作...
閱讀 2468·2019-08-30 15:53
閱讀 2581·2019-08-29 13:11
閱讀 2668·2019-08-29 12:45
閱讀 3495·2019-08-29 12:41
閱讀 2337·2019-08-26 10:14
閱讀 2166·2019-08-23 14:39
閱讀 2319·2019-08-23 12:38
閱讀 3383·2019-08-23 12:04