日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring装配Bean过程

發布時間:2025/5/22 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring装配Bean过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

主要流程:

1、讀取配置文件 2、實例化bean和填充bean屬性 這個粗略的流程感覺更像是一個需求,有了這個需求,那么spring內部是怎么處理的呢? 我們知道spring的兩個核心接口BeanFactory和ApplicationContext。BeanFactory主要定義容器的核心方法,ApplicationContext加以擴展,主要使用的還是ApplicationContext。在ApplicationContext的子類中,AbstractApplicationContext中的refresh()方法定義了容器加載配置文件及裝配Bean的過程。 AbstractApplicationContext#refresh()代碼如下: 1        // Prepare this context for refreshing. 2 //1 準備刷新工作 3 prepareRefresh(); 4 // Tell the subclass to refresh the internal bean factory. 5 // 2 實例化BeanFactory,將配置文件的信息裝入到容器的Bean定義的注冊表(BeanDefinitionRegistry中),此時Bean還未初始化 6 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 7 // Prepare the bean factory for use in this context. 8 // 3 準備BeanFactory 主要是加載一些類 9 prepareBeanFactory(beanFactory); 10 try { 11 // Allows post-processing of the bean factory in context subclasses. 12 // 4 留作子類實現 13 postProcessBeanFactory(beanFactory); 14 // Invoke factory processors registered as beans in the context. 15 // 5 調用工廠后處理器 16 invokeBeanFactoryPostProcessors(beanFactory); 17 // Register bean processors that intercept bean creation. 18 //6 注冊bean后處理器 19 registerBeanPostProcessors(beanFactory); 20 // Initialize message source for this context. 21 //7 初始化消息源 22 initMessageSource(); 23 // Initialize event multicaster for this context. 24 //8 初始化事件廣播器 25 initApplicationEventMulticaster(); 26 // Initialize other special beans in specific context subclasses. 27 //9 鉤子方法 28 onRefresh(); 29 // Check for listener beans and register them. 30 // 10 注冊監聽器 31 registerListeners(); 32 // Instantiate all remaining (non-lazy-init) singletons. 33 //11 完成bean實例化(除lazy-init),并放入緩存中 34 finishBeanFactoryInitialization(beanFactory); 35 // Last step: publish corresponding event. 36 // 12 廣播刷新事件 37 finishRefresh(); 38 } 其實直接粘貼代碼意義不大。重點還是要看refresh()函數的重點步驟。

一、prepareRefresh():準備工作。

在spring代碼中,很容易看到這樣的邏輯:就是在一個流程動作上下包裹著前后的動作。也就是說:在刷新動作時,會包裹刷新前動作和刷新后動作。這些動作可以有操作,也可以沒有。又如:在讀取xml文件加載bean時的一個操作(處理xml前,處理xml后): 1 preProcessXml(root); 2 parseBeanDefinitions(root, this.delegate); 3 postProcessXml(root); 這里主要記錄了一下容器開始時間,初始化了屬性資源。

二、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

作用:實例化BeanFactory,將配置文件的信息裝入到容器的Bean定義的注冊表(BeanDefinitionRegistry中),此時Bean還未初始化 其中,AbastractApplicationContext#obtainFreshBeanFactory()如下: 1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 2 refreshBeanFactory(); 3 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 4 if (logger.isDebugEnabled()) { 5 logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); 6 } 7 return beanFactory; 8 } 這里的getBeanFactory()方法用來獲取實例。重點是refreshBeanFactory()方法。 AbstractRefreshableApplicationContext#refreshBeanFactory() 1 protected final void refreshBeanFactory() throws BeansException { 2 if (hasBeanFactory()) { 3 destroyBeans(); 4 closeBeanFactory(); 5 } 6 try { 7 DefaultListableBeanFactory beanFactory = createBeanFactory(); 8 beanFactory.setSerializationId(getId()); 9 customizeBeanFactory(beanFactory); 10 loadBeanDefinitions(beanFactory); 11 synchronized (this.beanFactoryMonitor) { 12 this.beanFactory = beanFactory; 13 } 14 } 15 catch (IOException ex) { 16 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 17 } 18 } 如果存在BeanFacotry(),則銷毀。這里要保證在BeanDefinitionRegistry中存儲的是最新的xml對應的Bean映射。然后就到了loadBeanDefinitions(beanFactory)加載BeanDefinitions.中間跳過一些步驟,然后就到了XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource) 顯然是要從相應的資源(這里是XML中)讀取信息。截取一段信息: 1 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); 2 if (currentResources == null) { 3 currentResources = new HashSet<EncodedResource>(4); 4 this.resourcesCurrentlyBeingLoaded.set(currentResources); 5 } 6 if (!currentResources.add(encodedResource)) { 7 throw new BeanDefinitionStoreException( 8 "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); 9 } 10 try { 11 InputStream inputStream = encodedResource.getResource().getInputStream(); 12 try { 13 InputSource inputSource = new InputSource(inputStream); 14 if (encodedResource.getEncoding() != null) { 15 inputSource.setEncoding(encodedResource.getEncoding()); 16 } 17 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 18 } 19 finally { 20 inputStream.close(); 21 } 22 } 這里提到了doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法。點進去之后跟想的一樣,加載xml文件 1 try { 2 Document doc = doLoadDocument(inputSource, resource); 3 return registerBeanDefinitions(doc, resource); 4 } 接著跳過幾步就到了把xml中的DOM節點信息轉化為Bean對應屬性的方法 1 protected void doRegisterBeanDefinitions(Element root) { 2 BeanDefinitionParserDelegate parent = this.delegate; 3 this.delegate = createDelegate(getReaderContext(), root, parent); 4 if (this.delegate.isDefaultNamespace(root)) { 5 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); 6 if (StringUtils.hasText(profileSpec)) { 7 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( 8 profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); 9 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { 10 return; 11 } 12 } 13 } 14 preProcessXml(root); 15 parseBeanDefinitions(root, this.delegate); 16 postProcessXml(root); 17 this.delegate = parent; 18 } 這里到了parseBeanDefinitions(root, this.delegate)方法。也就是把root信息轉化為BeanDefinitions。點進去發現spring處理兩類節點:
  • 默認元素
  • 用戶自定義元素
1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 2 if (delegate.isDefaultNamespace(root)) { 3 NodeList nl = root.getChildNodes(); 4 for (int i = 0; i < nl.getLength(); i++) { 5 Node node = nl.item(i); 6 if (node instanceof Element) { 7 Element ele = (Element) node; 8 if (delegate.isDefaultNamespace(ele)) { 9 parseDefaultElement(ele, delegate); 10 } 11 else { 12 delegate.parseCustomElement(ele); 13 } 14 } 15 } 16 } 17 else { 18 delegate.parseCustomElement(root); 19 } 20 } 這里就是遍歷Element了。然后對每個節點判斷,如果是默認命名空間(http://www.springframework.org/schema/beans)下的元素,則使用parseDefaultElement(ele, delegate)方法,否則使用delegate.parseCustomElement(ele);方法。查看默認元素的方法:parseDefaultElement(ele, delegate); 1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 3 importBeanDefinitionResource(ele); 4 } 5 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 6 processAliasRegistration(ele); 7 } 8 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 9 processBeanDefinition(ele, delegate); 10 } 11 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { 12 // recurse 13 doRegisterBeanDefinitions(ele); 14 } 15 } 這里有四個節點:
  • public static final String IMPORT_ELEMENT = "import";
  • public static final String ALIAS_ELEMENT = "alias";
  • public static final String BEAN_ELEMENT = "bean";
  • public static final String NESTED_BEANS_ELEMENT = "beans";
這里再看一下DefaultBeanDefinitionDocumentReader#processBeanDefinition(ele, delegate)方法 1 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 里面有一個行代碼跳轉到registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 1 public static void registerBeanDefinition( 2 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 3 throws BeanDefinitionStoreException { 4 // Register bean definition under primary name. 5 String beanName = definitionHolder.getBeanName(); 6 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 7 // Register aliases for bean name, if any. 8 String[] aliases = definitionHolder.getAliases(); 9 if (aliases != null) { 10 for (String aliase : aliases) { 11 registry.registerAlias(beanName, aliase); 12 } 13 } 14 } 這里有一個地方需要注意:前面提到過的BeanDefinitionRegistry,也就是把配置信息裝入到BeanDefinitionRegistry中。進入 registerBeanDefinition方法中如下: 1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 2 throws BeanDefinitionStoreException { 3 Assert.hasText(beanName, "'beanName' must not be empty"); 4 Assert.notNull(beanDefinition, "BeanDefinition must not be null"); 5 this.beanDefinitionMap.put(beanName, beanDefinition); 6 } 這里我們可以看到,beanName和beanDefinition裝入到了一個Map中。這個map的定義如下: 1 /** Map of bean definition objects, keyed by bean name */ 2 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64); 其中,key位bean的名字,value為BeanDefinition。 這個時候的Bean還是沒有屬性的。

三、prepareBeanFactory(beanFactory):加載一些類資源

四、postProcessBeanFactory(beanFactory):留作子類實現,這里為空

五、invokeBeanFactoryPostProcessors(beanFactory):調用BeanFactoryPostProcessor接口處理beanFactory.

這個方法的參數是beanFactory,它包含了在xml定義的bean信息。調用這個方法,可以在讀取xmlbean信息之后以及實例化bean之前修改bean定義的屬性。 spring中,有內置的一些BeanFactoryPostProcessor實現類,常用的有:
  • org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:屬性占位符(${user})
  • org.springframework.beans.factory.config.PropertyOverrideConfigurer
  • org.springframework.beans.factory.config.CustomEditorConfigurer:用來注冊自定義的屬性編輯器
通過這幾個實現類可以了解到這個接口會起到的一些作用:對占位符進行處理等 其中:BeanFactoryPostProcessor接口定義如下: 1 public interface BeanFactoryPostProcessor { 2 /** 3 *在其標準初始化后修改應用程序上下文的內部bean工廠。 所有bean定義都將被加載,但是沒有bean將被實例化。 4 *這允許覆蓋或添加屬性,即使是急切地初始化bean。 5 */ 6 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; 7 } 這一步的重點是理解BeanFactoryPostProcessor接口的作用。實現這個接口,可以實現修改bean屬性的功能。也就是說,Spring允許BeanFactoryPostProcessor在容器實例化任何其它bean之前讀取配置元數據,并可以根據需要進行修改,例如可以把bean的scope從singleton改為prototype,也可以把property的值給修改掉。可以同時配置多個BeanFactoryPostProcessor,并通過設置'order'屬性來控制各個BeanFactoryPostProcessor的執行次序。 這里摘抄一個網上的例子(原地址:http://blog.csdn.net/caihaijiang/article/details/35552859)實現一個自定義的BeanFactoryPostProcessor實現類來實現動態修改Bean屬性功能。 1.在配置文件中定義一個名為MyBean的bean,這里只提供了兩個簡單的屬性desc和mark。以及定義一個實現BeanFactoryPostProcessor接口的類MyBeanFactoryPostProcessorImpl 1 <bean id="myBean" class="uodut.spring3x.MyBean"> 2 <property name="desc" value="這里通過自定義MyBeanFactoryPostProcessorImpl來動態改變mark的值"></property> 3 <property name="mark" value="initMark"></property> 4 </bean> 5 <bean id="myBeanFactoryPostProcessor" class="uodut.spring3x.MyBeanFactoryPostProcessorImpl"/> 2.MyBeanFactoryPostProcessorImpl類實現如下: 1 public class MyBeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor { 2 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 3 BeanDefinition bd = beanFactory.getBeanDefinition("myBean"); 4 System.out.println("屬性值:" + bd.getPropertyValues().toString()); 5 MutablePropertyValues propertyValues = bd.getPropertyValues(); 6 if (propertyValues.contains("mark")) { 7 propertyValues.addPropertyValue("mark", "initMark->afterMark"); 8 } 9 } 10 } 3.定義一個測試方法來測試 1 public void test1(){ 2 ApplicationContext context = new ClassPathXmlApplicationContext("BeanTest.xml"); 3 MyBean bean = (MyBean) context.getBean("myBean"); 4 System.out.println("描述:" + bean.getDesc()); 5 System.out.println("備注:" + bean.getMark()); 6 } 運行后輸出結果如下:(可以看到mark屬性的值從initMark變成了initMark->afterMark) 1 屬性值:PropertyValues: length=2; bean property 'desc'; bean property 'mark' 2 描述:這里通過自定義MyBeanFactoryPostProcessorImpl來動態改變mark的值 3 備注:initMark->afterMark

六、registerBeanPostProcessors(beanFactory);注冊BeanPostProcessors

BeanPostProcessor,可以在spring容器實例化bean之后,在執行bean的初始化方法前后,添加一些自己的處理邏輯。這里說的初始化方法,指的是下面兩種: 1)bean實現了InitializingBean接口,對應的方法為afterPropertiesSet 2)在bean定義的時候,通過init-method設置的方法 注意:BeanPostProcessor是在spring容器加載了bean的定義文件并且實例化bean之后執行的。BeanPostProcessor的執行順序是在BeanFactoryPostProcessor之后。 這里在上面的例子基礎上修改: 1 public class MyBean implements InitializingBean{ 2 public void afterPropertiesSet() throws Exception { 3 System.out.println("調用afterPropertiesSet方法"); 4 this.desc = "在初始化方法中修改之后的描述信息"; 5 } 6 } 聲明MyBeanPostProcessorImpl實現BeanPostProcessor 1 public class MyBeanPostProcessorImpl implements BeanPostProcessor { 2 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 3 System.out.println("BeanPostProcessor,對象" + beanName + "調用初始化方法之前的數據: " + bean.toString()); 4 return bean; 5 } 6 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 7 System.out.println("BeanPostProcessor,對象" + beanName + "調用初始化方法之后的數據:" + bean.toString()); 8 return bean; 9 } 10 } 運行測試用例: 1 屬性值:PropertyValues: length=2; bean property 'desc'; bean property 'mark' 2 調用setDesc方法 3 調用setMark方法 4 BeanPostProcessor,對象myBean調用初始化方法之前的數據: [描述:原始描述信息, 備注:initMark->afterMark] 5 調用afterPropertiesSet方法 6 調用initMethod方法 7 BeanPostProcessor,對象myBean調用初始化方法之后的數據:[描述:在初始化方法中修改之后的描述信息, 備注:initMark->afterMark] 8 描述:在初始化方法中修改之后的描述信息 9 備注:initMark->afterMark 可以看到在初始化方法前后會調用BeanPostProcessor接口的兩個方法。

七、initMessageSource();初始化消息源

八、initApplicationEventMulticaster();初始化應用上下文事件廣播

九、onRefresh();初始化其他特殊的bean,這是一個鉤子方法。

十、registerListeners();注冊監聽器

十一、finishBeanFactoryInitialization(beanFactory);初始化所有單實例的Bean,使用lazy-init的bean除外。初始化Bean后,將它們放入Spring容器的緩存中。

十二、finishRefresh();創建上下文刷新事件,事件廣播器負責將這些事件廣播到每個注冊的事件監聽器中。

spring容器從加載配置文件到創建出一個完整的Bean的流程

1、ResourceLoader從存儲介質中加載Spring配置信息,并使用Resource表示該配置文件的資源 2、BeanDefinitionReader讀取Resource所指向的配置文件資源,然后解析配置文件。配置文件中的每個<bean>解析成一個BeanDefinition對象,并保存到BeanDefinitionRegistry中。 3、容器掃描BeanDefinitionRegistry中的BeanDefinition,使用java反射機制識別出實現BeanFactoryPostProcessor接口的Bean,然后調用BeanFactoryPostProcessor的實現類對BeanDefinitionRegistry中的BeanDefinition進行加工處理。主要作以下兩個工作: 1)對使用到占位符的<bean>元素標簽進行解析,得到最終的配置值。這意味著對半成品式的BeanDefinition對象進行加工處理并得到成品的BeanDefinition對象。(這里用到了前文提到的PropertyPlaceholderConfigurer) 2)對BeanDefinitionRegistry中的BeanDefinition進行掃描,通過反射機制找出所有屬性編輯器的Bean(實現java.beans.PropertyEditor接口的Bean),并自動將它們注冊到spring容器的屬性編輯器注冊表中(PropertyEditorRegistry) 4、Spring容器從BeanDefinitionRegistry中取出加工后的BeanDefinition,并調用InstantiationStrategy進行Bean實例化的工作。 5、在實例化Bean時,Spring容器使用BeanWrapper對Bean進行封裝,它結合該Bean的BeanDefinition以及容器中屬性編輯器,完成Bean屬性的設置工作。 6、BeanPostProcess對完成屬性設置的Bean進行后續加工,裝配出一個準備就緒的Bean。 參考資料:
  • 《spring 3.x企業應用開發實戰》
  • http://blog.csdn.net/caihaijiang/article/details/35552859?

轉載于:https://www.cnblogs.com/uodut/p/7218516.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的spring装配Bean过程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚州黄色网址 | 姝姝窝人体www聚色窝 | 欧美精品在线免费观看 | 神马午夜51| 国产无套粉嫩白浆内谢 | 成人性生交大片免费看vrv66 | 青娱乐导航 | 欧美一区三区二区在线观看 | 国产美女免费看 | 天堂网av手机版 | 日韩另类 | 在线视频激情小说 | 亚洲第一成人在线 | 成年人视频在线播放 | av九九九 | 亚洲免费视频播放 | 免费观看h片 | 欧美国产大片 | 亚洲欧美国产一区二区三区 | 偷拍欧美亚洲 | 欧美性猛交ⅹxxx乱大交3 | 超碰在线人 | 999色综合| 六月色| 永久免费av无码网站性色av | 青娱乐最新地址 | 国产精品无码一区二区三 | 欧美你懂的 | 91免费视频免费版 | 粉豆av| 午夜不卡av | 国产三区视频 | 日韩成人免费av | 久草免费在线观看 | 欧美xxxx在线 | 蜜臀av无码精品人妻色欲 | jizz成人| 亚洲一区二区三区免费视频 | 日韩视频在线观看一区 | 久久伊人草| 一区二区日韩 | 在线不卡欧美 | 日本女优网址 | 国产免费黄色小视频 | 国产精品日韩欧美 | 奇米影视av | 亚洲精品国产精品乱码不99热 | 奇米影音| 网站在线看 | 亚洲网址在线 | 怒海潜沙秦岭神树 | a级片视频网站 | 日韩av成人在线 | 日日碰狠狠添天天爽无码av | 日韩av无码一区二区三区 | 爱就操| 欧美伊人影院 | 亚洲一二区视频 | 欧美中文字幕一区二区三区 | 张柏芝54张无删码视频 | 欧美一级不卡视频 | 视频一区二区三区在线观看 | 欧美一区二区三区在线免费观看 | 欧美韩日一区二区 | 国产农村妇女精品一二区 | 午夜不卡福利视频 | 久久国产亚洲精品无码 | 黄色高清视频 | 国产一区二区视频免费观看 | 一亲二脱三插 | 国产精品专区在线观看 | 一级全黄裸体免费观看视频 | 五月婷婷小说 | 日韩中文一区 | 国产在线一级片 | 免费高清毛片 | 求av网站| 玖玖玖国产精品 | 91亚洲精品一区二区乱码 | 日韩综合在线观看 | a∨视频 | 日韩av免费在线播放 | 91精品一区二区三区在线观看 | 日韩在线一卡 | 国产21页| 欧美色女人 | 亚洲国产精华液网站w | 四虎国产精品永久在线国在线 | 给我看高清的视频在线观看 | 色版视频 | a天堂资源在线 | 青青视频在线播放 | 日韩av在线一区 | 天天看片天天操 | 精久久久 | 97少妇| 精品国产系列 | 久久成人国产精品 | 亚洲色图丝袜美腿 |