<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 【第三章】 DI 之 3.4 Bean的作用域 ——跟我學spring3 ## 3.4? Bean的作用域 什么是作用域呢?即“scope”,在面向對象程序設計中一般指對象或變量之間的可見范圍。而在Spring容器中是指其創建的Bean對象相對于其他Bean對象的請求可見范圍。 Spring提供“singleton”和“prototype”兩種基本作用域,另外提供“request”、“session”、“global session”三種web作用域;Spring還允許用戶定制自己的作用域。 ### 3.4.1? 基本的作用域 **一、singleton:**指“singleton”作用域的Bean只會在每個Spring IoC容器中存在一個實例,而且其完整生命周期完全由Spring容器管理。對于所有獲取該Bean的操作Spring容器將只返回同一個Bean。 > | > > GoF單例設計模式指“**保證一個類僅有一個實例,并提供一個訪問它的全局訪問點**”,介紹了兩種實現:通過在類上定義靜態屬性保持該實例和通過注冊表方式。 > > | **1)通過在類上定義靜態屬性保持該實例:**一般指一個Java虛擬機 ClassLoader裝載的類只有一個實例,一般通過類靜態屬性保持該實例,這樣就造成需要單例的類都需要按照單例設計模式進行編碼;Spring沒采用這種方式,因為該方式屬于侵入式設計;代碼樣例如下: 1. package?cn.javass.spring.chapter3.bean;?? 2. public?class?Singleton?{?? 3. //1.私有化構造器?? 4. private?Singleton()?{}?? 5. //2.單例緩存者,惰性初始化,第一次使用時初始化?? 6. private?static?class?InstanceHolder?{?? 7. private?static?final?Singleton?INSTANCE?=?new?Singleton();?? 8. }?? 9. //3.提供全局訪問點?? 10. public?static?Singleton?getInstance()?{?? 11. return?InstanceHolder.INSTANCE;?? 12. }?? 13. //4.提供一個計數器來驗證一個ClassLoader一個實例?? 14. private?int?counter=0;?? 15. }?? 以上定義個了個單例類,首先要私有化類構造器;其次使用InstanceHolder靜態內部類持有單例對象,這樣可以得到惰性初始化好處;最后提供全局訪問點getInstance,使得需要該單例實例的對象能獲取到;我們在此還提供了一個counter計數器來驗證一個ClassLoader一個實例。具體一個ClassLoader有一個單例實例測試請參考代碼“cn.javass.spring.chapter3\. SingletonTest”中的“testSingleton”測試方法,里邊詳細演示了一個ClassLoader有一個單例實例。 1)??**通過注冊表方式:**?首先將需要單例的實例通過唯一鍵注冊到注冊表,然后通過鍵來獲取單例,讓我們直接看實現吧,注意本注冊表實現了Spring接口“SingletonBeanRegistry”,該接口定義了操作共享的單例對象,Spring容器實現將實現此接口;所以共享單例對象通過“registerSingleton”方法注冊,通過“getSingleton”方法獲取,消除了編程方式單例,注意在實現中不考慮并發: 1. package?cn.javass.spring.chapter3;?? 2. import?java.util.HashMap;?? 3. import?java.util.Map;?? 4. import?org.springframework.beans.factory.config.SingletonBeanRegistry;?? 5. public?class?SingletonBeanRegister?implements?SingletonBeanRegistry?{?? 6. //單例Bean緩存池,此處不考慮并發?? 7. private?final?Map&lt;String,?Object&gt;?BEANS?=?new?HashMap&lt;String,?Object&gt;();?? 8. public?boolean?containsSingleton(String?beanName)?{?? 9. return?BEANS.containsKey(beanName);?? 10. }?? 11. public?Object?getSingleton(String?beanName)?{?? 12. return?BEANS.get(beanName);?? 13. }?? 14. @Override?? 15. public?int?getSingletonCount()?{?? 16. return?BEANS.size();?? 17. }?? 18. @Override?? 19. public?String[]?getSingletonNames()?{?? 20. return?BEANS.keySet().toArray(new?String[0]);?? 21. }?? 22. @Override?? 23. public?void?registerSingleton(String?beanName,?Object?bean)?{?? 24. if(BEANS.containsKey(beanName))?{?? 25. throw?new?RuntimeException("["?+?beanName?+?"]?已存在");?? 26. }?? 27. BEANS.put(beanName,?bean);?? 28. }?? 29. }?? Spring是注冊表單例設計模式的實現,消除了編程式單例,而且對代碼是非入侵式。 接下來讓我們看看在Spring中如何配置單例Bean吧,在Spring容器中如果沒指定作用域默認就是“singleton”,配置方式通過scope屬性配置,具體配置如下: 1. &lt;bean??class="cn.javass.spring.chapter3.bean.Printer"?scope="singleton"/&gt;?? Spring管理單例對象在Spring容器中存儲如圖3-5所示,Spring不僅會緩存單例對象,Bean定義也是會緩存的,對于惰性初始化的對象是在首次使用時根據Bean定義創建并存放于單例緩存池。 ![](https://box.kancloud.cn/2016-05-12_57348071dc556.JPG) 圖3-5 單例處理 **二、prototype:**即原型,指每次向Spring容器請求獲取Bean都返回一個全新的Bean,相對于“singleton”來說就是不緩存Bean,每次都是一個根據Bean定義創建的全新Bean。 > GoF原型設計模式,指用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。 Spring中的原型和GoF中介紹的原型含義是不一樣的: GoF通過用原型實例指定創建對象的種類,而Spring容器用Bean定義指定創建對象的種類; GoF通過拷貝這些原型創建新的對象,而Spring容器根據Bean定義創建新對象。 其相同地方都是根據某些東西創建新東西,而且GoF原型必須顯示實現克隆操作,屬于侵入式,而Spring容器只需配置即可,屬于非侵入式。 接下來讓我們看看Spring如何實現原型呢? 1)首先讓我們來定義Bean“原型”:Bean定義,所有對象將根據Bean定義創建;在此我們只是簡單示例一下,不會涉及依賴注入等復雜實現:BeanDefinition類定義屬性“class”表示原型類,“id”表示唯一標識,“scope”表示作用域,具體如下: 1. package?cn.javass.spring.chapter3;?? 2. public?class?BeanDefinition?{?? 3. //單例?? 4. public?static?final?int?SCOPE_SINGLETON?=?0;?? 5. //原型?? 6. public?static?final?int?SCOPE_PROTOTYPE?=?1;?? 7. //唯一標識?? 8. private?String?id;?? 9. //class全限定名?? 10. private?String?clazz;?? 11. //作用域?? 12. private?int?scope?=?SCOPE_SINGLETON;?? 13. //鑒于篇幅,省略setter和getter方法;?? 14. }?? 2)接下來讓我們看看Bean定義注冊表,類似于單例注冊表: 1. package?cn.javass.spring.chapter3;?? 2. import?java.util.HashMap;?? 3. import?java.util.Map;?? 4. public?class?BeanDifinitionRegister?{?? 5. //bean定義緩存,此處不考慮并發問題?? 6. private?final?Map&lt;String,?BeanDefinition&gt;?DEFINITIONS?=?? 7. new?HashMap&lt;String,?BeanDefinition&gt;();?? 8. public?void?registerBeanDefinition(String?beanName,?BeanDefinition?bd)?{?? 9. //1.本實現不允許覆蓋Bean定義?? 10. if(DEFINITIONS.containsKey(bd.getId()))?{?? 11. throw?new?RuntimeException("已存在Bean定義,此實現不允許覆蓋");?? 12. }?? 13. //2.將Bean定義放入Bean定義緩存池?? 14. DEFINITIONS.put(bd.getId(),?bd);?? 15. }?? 16. public?BeanDefinition?getBeanDefinition(String?beanName)?{?? 17. return?DEFINITIONS.get(beanName);?? 18. }?? 19. public?boolean?containsBeanDefinition(String?beanName)?{???????? 20. return?DEFINITIONS.containsKey(beanName);?? 21. }?? 22. }?? 3)接下來應該來定義BeanFactory了: 1. package?cn.javass.spring.chapter3;?? 2. import?org.springframework.beans.factory.config.SingletonBeanRegistry;?? 3. public?class?DefaultBeanFactory?{?? 4. //Bean定義注冊表?? 5. private?BeanDifinitionRegister?DEFINITIONS?=?new?BeanDifinitionRegister();?? 7. //單例注冊表?? 8. private?final?SingletonBeanRegistry?SINGLETONS?=?new?SingletonBeanRegister();?? 10. public?Object?getBean(String?beanName)?{?? 11. //1.驗證Bean定義是否存在?? 12. if(!DEFINITIONS.containsBeanDefinition(beanName))?{?? 13. throw?new?RuntimeException("不存在["?+?beanName?+?"]Bean定義");?? 14. }?? 15. //2.獲取Bean定義?? 16. BeanDefinition?bd?=?DEFINITIONS.getBeanDefinition(beanName);?? 17. //3.是否該Bean定義是單例作用域?? 18. if(bd.getScope()?==?BeanDefinition.SCOPE_SINGLETON)?{?? 19. //3.1?如果單例注冊表包含Bean,則直接返回該Bean?? 20. if(SINGLETONS.containsSingleton(beanName))?{?? 21. return?SINGLETONS.getSingleton(beanName);?? 22. }?? 23. //3.2單例注冊表不包含該Bean,?? 24. //則創建并注冊到單例注冊表,從而緩存?? 25. SINGLETONS.registerSingleton(beanName,?createBean(bd));?? 26. return?SINGLETONS.getSingleton(beanName);?? 27. }?? 28. //4.如果是原型Bean定義,則直接返回根據Bean定義創建的新Bean,?? 29. //每次都是新的,無緩存?? 30. if(bd.getScope()?==?BeanDefinition.SCOPE_PROTOTYPE)?{?? 31. return?createBean(bd);?? 32. }?? 33. //5.其他情況錯誤的Bean定義?? 34. throw?new?RuntimeException("錯誤的Bean定義");?? 35. }?? 1. public?void?registerBeanDefinition(BeanDefinition?bd)?{?? 2. DEFINITIONS.registerBeanDefinition(bd.getId(),?bd);?? 3. }?? 5. private?Object?createBean(BeanDefinition?bd)?{?? 6. //根據Bean定義創建Bean?? 7. try?{?? 8. Class?clazz?=?Class.forName(bd.getClazz());?? 9. //通過反射使用無參數構造器創建Bean?? 10. return?clazz.getConstructor().newInstance();?? 11. }?catch?(ClassNotFoundException?e)?{?? 12. throw?new?RuntimeException("沒有找到Bean["?+?bd.getId()?+?"]類");?? 13. }?catch?(Exception?e)?{?? 14. throw?new?RuntimeException("創建Bean["?+?bd.getId()?+?"]失敗");?? 15. }?? 16. }?? 其中方法getBean用于獲取根據beanName對于的Bean定義創建的對象,有單例和原型兩類Bean;registerBeanDefinition方法用于注冊Bean定義,私有方法createBean用于根據Bean定義中的類型信息創建Bean。 3)測試一下吧,在此我們只測試原型作用域Bean,對于每次從Bean工廠中獲取的Bean都是一個全新的對象,代碼片段(BeanFatoryTest)如下: 1. @Test?? 2. public?void?testPrototype?()?throws?Exception?{?? 3. //1.創建Bean工廠?? 4. DefaultBeanFactory?bf?=?new?DefaultBeanFactory();?? 5. //2.創建原型?Bean定義?? 6. BeanDefinition?bd?=?new?BeanDefinition();?? 7. bd.setId("bean");?? 8. bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);?? 9. bd.setClazz(HelloImpl2.class.getName());?? 10. bf.registerBeanDefinition(bd);?? 11. //對于原型Bean每次應該返回一個全新的Bean?? 12. System.out.println(bf.getBean("bean")?!=?bf.getBean("bean"));?? 13. }?? 最后讓我們看看如何在Spring中進行配置吧,只需指定&lt;bean&gt;標簽屬性“scope”屬性為“prototype”即可: 1. &lt;bean?class="cn.javass.spring.chapter3.bean.Printer"?scope="prototype"/&gt;?? Spring管理原型對象在Spring容器中存儲如圖3-6所示,Spring不會緩存原型對象,而是根據Bean定義每次請求返回一個全新的Bean: ![](https://box.kancloud.cn/2016-05-13_5735471cc6084.JPG) 圖3-6 原型處理 單例和原型作用域我們已經講完,接下來讓我們學習一些在Web應用中有哪些作用域: ### 3.4.2? Web應用中的作用域 在Web應用中,我們可能需要將數據存儲到request、session、?session。因此Spring提供了三種Web作用域:request、session、globalSession。 **一、request作用域:表示每個請求需要容器創建一個全新Bean。**比如提交表單的數據必須是對每次請求新建一個Bean來保持這些表單數據,請求結束釋放這些數據。 **二、session作用域:表示每個會話需要容器創建一個全新Bean。**比如對于每個用戶一般會有一個會話,該用戶的用戶信息需要存儲到會話中,此時可以將該Bean配置為web作用域。 **三、globalSession:**類似于session作用域,只是其用于portlet環境的web應用。如果在非portlet環境將視為session作用域。 配置方式和基本的作用域相同,只是必須要有web環境支持,并配置相應的容器監聽器或攔截器從而能應用這些作用域,我們會在集成web時講解具體使用,大家只需要知道有這些作用域就可以了。 ### 3.4.4?自定義作用域 在日常程序開發中,幾乎用不到自定義作用域,除非又必要才進行自定義作用域。 首先讓我們看下Scope接口吧: 1. package?org.springframework.beans.factory.config;?? 2. import?org.springframework.beans.factory.ObjectFactory;?? 3. public?interface?Scope?{?? 4. Object?get(String?name,?ObjectFactory&lt;?&gt;?objectFactory);?? 5. Object?remove(String?name);?? 6. void?registerDestructionCallback(String?name,?Runnable?callback);?? 7. Object?resolveContextualObject(String?key);?? 8. String?getConversationId();?? 9. }?? 1)**Object get(String name, ObjectFactory&lt;?&gt; objectFactory):**用于從作用域中獲取Bean,其中參數objectFactory是當在當前作用域沒找到合適Bean時使用它創建一個新的Bean; 2)**void registerDestructionCallback(String name, Runnable callback):**用于注冊銷毀回調,如果想要銷毀相應的對象則由Spring容器注冊相應的銷毀回調,而由自定義作用域選擇是不是要銷毀相應的對象; 3)**Object resolveContextualObject(String key):**用于解析相應的上下文數據,比如request作用域將返回request中的屬性。 4)**String getConversationId():**作用域的會話標識,比如session作用域將是sessionId。 1. package?cn.javass.spring.chapter3;?? 2. import?java.util.HashMap;?? 3. import?java.util.Map;?? 4. import?org.springframework.beans.factory.ObjectFactory;?? 5. import?org.springframework.beans.factory.config.Scope;?? 6. public?class?ThreadScope?implements?Scope?{?? 7. private?final?ThreadLocal&lt;Map&lt;String,?Object&gt;&gt;?THREAD_SCOPE?=?? 8. new?ThreadLocal&lt;Map&lt;String,?Object&gt;&gt;()?{?? 9. protected?Map&lt;String,?Object&gt;?initialValue()?{?? 10. //用于存放線程相關Bean?? 11. return?new?HashMap&lt;String,?Object&gt;();?? 12. }?? 13. };?? 讓我們來實現個簡單的thread作用域,該作用域內創建的對象將綁定到ThreadLocal內。 1. @Override?? 2. public?Object?get(String?name,?ObjectFactory&lt;?&gt;?objectFactory)?{?? 3. //如果當前線程已經綁定了相應Bean,直接返回?? 4. if(THREAD_SCOPE.get().containsKey(name))?{?? 5. return?THREAD_SCOPE.get().get(name);?? 6. }?? 7. //使用objectFactory創建Bean并綁定到當前線程上?? 8. THREAD_SCOPE.get().put(name,?objectFactory.getObject());?? 9. return?THREAD_SCOPE.get().get(name);?? 10. }?? 11. @Override?? 12. public?String?getConversationId()?{?? 13. return?null;?? 14. }?? 15. @Override?? 16. public?void?registerDestructionCallback(String?name,?Runnable?callback)?{?? 17. //此處不實現就代表類似proytotype,容器返回給用戶后就不管了?? 18. }?? 19. @Override?? 20. public?Object?remove(String?name)?{?? 21. return?THREAD_SCOPE.get().remove(name);?? 22. }?? 23. @Override?? 24. public?Object?resolveContextualObject(String?key)?{?? 25. return?null;?? 26. }?? 27. }?? Scope已經實現了,讓我們將其注冊到Spring容器,使其發揮作用: 1. &lt;bean?class="org.springframework.beans.factory.config.CustomScopeConfigurer"&gt;?? 2. &lt;property?name="scopes"&gt;?? 3. &lt;map&gt;&lt;entry&gt;?? 4. &lt;!--?指定scope關鍵字?--&gt;&lt;key&gt;&lt;value&gt;thread&lt;/value&gt;&lt;/key&gt;?? 5. &lt;!--?scope實現?--&gt;??????&lt;bean?class="cn.javass.spring.chapter3.ThreadScope"/&gt;?? 6. &lt;/entry&gt;&lt;/map&gt;?????? 7. &lt;/property&gt;?? 8. &lt;/bean&gt;?? 通過CustomScopeConfigurer的scopes屬性注冊自定義作用域實現,在此需要指定使用作用域的關鍵字“thread”,并指定自定義作用域實現。來讓我們來定義一個“thread”作用域的Bean,配置(chapter3/threadScope.xml)如下: 1. &lt;bean?id="helloApi"?? 2. class="cn.javass.spring.chapter2.helloworld.HelloImpl"?? 3. scope="thread"/&gt;?? 最后測試(cn.javass.spring.chapter3.ThreadScopeTest)一下吧,首先在一個線程中測試,在同一線程中獲取的Bean應該是一樣的;再讓我們開啟兩個線程,然后應該這兩個線程創建的Bean是不一樣: 自定義作用域實現其實是非常簡單的,其實復雜的是如果需要銷毀Bean,自定義作用域如何正確的銷毀Bean。 原創內容 轉載請注明出處【[http://sishuok.com/forum/blogPost/list/2454.html](http://sishuok.com/forum/blogPost/list/2454.html#7084)】
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看