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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

DI 之 3.4 Bean的作用域(捌)

發(fā)布時間:2023/12/31 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DI 之 3.4 Bean的作用域(捌) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

3.4? Bean的作用域

?????? 什么是作用域呢?即“scope”,在面向對象程序設計中一般指對象或變量之間的可見范圍。而在Spring容器中是指其創(chuàng)建的Bean對象相對于其他Bean對象的請求可見范圍。

Spring提供“singleton”和“prototype”兩種基本作用域,另外提供“request”、“session”、“global session”三種web作用域;Spring還允許用戶定制自己的作用域。

3.4.1? 基本的作用域

???????一、singleton指“singleton”作用域的Bean只會在每個Spring IoC容器中存在一個實例,而且其完整生命周期完全由Spring容器管理。對于所有獲取該Bean的操作Spring容器將只返回同一個Bean。

GoF單例設計模式指“保證一個類僅有一個實例,并提供一個訪問它的全局訪問點”,介紹了兩種實現(xiàn):通過在類上定義靜態(tài)屬性保持該實例和通過注冊表方式。

1)通過在類上定義靜態(tài)屬性保持該實例:一般指一個Java虛擬機 ClassLoader裝載的類只有一個實例,一般通過類靜態(tài)屬性保持該實例,這樣就造成需要單例的類都需要按照單例設計模式進行編碼;Spring沒采用這種方式,因為該方式屬于侵入式設計;代碼樣例如下:

package cn.javass.spring.chapter3.bean; public class Singleton { //1.私有化構造器 private Singleton() {} //2.單例緩存者,惰性初始化,第一次使用時初始化 private static class InstanceHolder { private static final Singleton INSTANCE = new Singleton(); } //3.提供全局訪問點 public static Singleton getInstance() { return InstanceHolder.INSTANCE; } //4.提供一個計數(shù)器來驗證一個ClassLoader一個實例 private int counter=0; }

以上定義個了個單例類,首先要私有化類構造器;其次使用InstanceHolder靜態(tài)內部類持有單例對象,這樣可以得到惰性初始化好處;最后提供全局訪問點getInstance,使得需要該單例實例的對象能獲取到;我們在此還提供了一個counter計數(shù)器來驗證一個ClassLoader一個實例。具體一個ClassLoader有一個單例實例測試請參考代碼“cn.javass.spring.chapter3. SingletonTest”中的“testSingleton”測試方法,里邊詳細演示了一個ClassLoader有一個單例實例。

?

1)??通過注冊表方式:?首先將需要單例的實例通過唯一鍵注冊到注冊表,然后通過鍵來獲取單例,讓我們直接看實現(xiàn)吧,注意本注冊表實現(xiàn)了Spring接口“SingletonBeanRegistry”,該接口定義了操作共享的單例對象,Spring容器實現(xiàn)將實現(xiàn)此接口;所以共享單例對象通過“registerSingleton”方法注冊,通過“getSingleton”方法獲取,消除了編程方式單例,注意在實現(xiàn)中不考慮并發(fā):

package cn.javass.spring.chapter3; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.config.SingletonBeanRegistry; public class SingletonBeanRegister implements SingletonBeanRegistry { //單例Bean緩存池,此處不考慮并發(fā) private final Map<String, Object> BEANS = new HashMap<String, Object>(); public boolean containsSingleton(String beanName) { return BEANS.containsKey(beanName); } public Object getSingleton(String beanName) { return BEANS.get(beanName); } @Override public int getSingletonCount() { return BEANS.size(); } @Override public String[] getSingletonNames() { return BEANS.keySet().toArray(new String[0]); } @Override public void registerSingleton(String beanName, Object bean) { if(BEANS.containsKey(beanName)) { throw new RuntimeException("[" + beanName + "] 已存在"); } BEANS.put(beanName, bean); } }

Spring是注冊表單例設計模式的實現(xiàn),消除了編程式單例,而且對代碼是非入侵式。

接下來讓我們看看在Spring中如何配置單例Bean吧,在Spring容器中如果沒指定作用域默認就是“singleton”,配置方式通過scope屬性配置,具體配置如下:

<bean class="cn.javass.spring.chapter3.bean.Printer" scope="singleton"/>

Spring管理單例對象在Spring容器中存儲如圖3-5所示,Spring不僅會緩存單例對象,Bean定義也是會緩存的,對于惰性初始化的對象是在首次使用時根據(jù)Bean定義創(chuàng)建并存放于單例緩存池。

圖3-5 單例處理

?

二、prototype即原型,指每次向Spring容器請求獲取Bean都返回一個全新的Bean,相對于“singleton”來說就是不緩存Bean,每次都是一個根據(jù)Bean定義創(chuàng)建的全新Bean。

GoF原型設計模式,指用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。

Spring中的原型和GoF中介紹的原型含義是不一樣的:

? ? ? ? ?GoF通過用原型實例指定創(chuàng)建對象的種類,而Spring容器用Bean定義指定創(chuàng)建對象的種類;

? ? ? ? ?GoF通過拷貝這些原型創(chuàng)建新的對象,而Spring容器根據(jù)Bean定義創(chuàng)建新對象。

其相同地方都是根據(jù)某些東西創(chuàng)建新東西,而且GoF原型必須顯示實現(xiàn)克隆操作,屬于侵入式,而Spring容器只需配置即可,屬于非侵入式。

?

接下來讓我們看看Spring如何實現(xiàn)原型呢?

?

1)首先讓我們來定義Bean“原型”:Bean定義,所有對象將根據(jù)Bean定義創(chuàng)建;在此我們只是簡單示例一下,不會涉及依賴注入等復雜實現(xiàn):BeanDefinition類定義屬性“class”表示原型類,“id”表示唯一標識,“scope”表示作用域,具體如下:

package cn.javass.spring.chapter3; public class BeanDefinition { //單例 public static final int SCOPE_SINGLETON = 0; //原型 public static final int SCOPE_PROTOTYPE = 1; //唯一標識 private String id; //class全限定名 private String clazz; //作用域 private int scope = SCOPE_SINGLETON; //鑒于篇幅,省略setter和getter方法; }

2)接下來讓我們看看Bean定義注冊表,類似于單例注冊表:

package cn.javass.spring.chapter3; import java.util.HashMap; import java.util.Map; public class BeanDifinitionRegister { //bean定義緩存,此處不考慮并發(fā)問題 private final Map<String, BeanDefinition> DEFINITIONS = new HashMap<String, BeanDefinition>(); public void registerBeanDefinition(String beanName, BeanDefinition bd) { //1.本實現(xiàn)不允許覆蓋Bean定義 if(DEFINITIONS.containsKey(bd.getId())) { throw new RuntimeException("已存在Bean定義,此實現(xiàn)不允許覆蓋"); } //2.將Bean定義放入Bean定義緩存池 DEFINITIONS.put(bd.getId(), bd); } public BeanDefinition getBeanDefinition(String beanName) { return DEFINITIONS.get(beanName); } public boolean containsBeanDefinition(String beanName) { return DEFINITIONS.containsKey(beanName); } }

3)接下來應該來定義BeanFactory了:

package cn.javass.spring.chapter3; import org.springframework.beans.factory.config.SingletonBeanRegistry; public class DefaultBeanFactory { //Bean定義注冊表 private BeanDifinitionRegister DEFINITIONS = new BeanDifinitionRegister(); //單例注冊表 private final SingletonBeanRegistry SINGLETONS = new SingletonBeanRegister(); public Object getBean(String beanName) { //1.驗證Bean定義是否存在 if(!DEFINITIONS.containsBeanDefinition(beanName)) { throw new RuntimeException("不存在[" + beanName + "]Bean定義"); } //2.獲取Bean定義 BeanDefinition bd = DEFINITIONS.getBeanDefinition(beanName); //3.是否該Bean定義是單例作用域 if(bd.getScope() == BeanDefinition.SCOPE_SINGLETON) { //3.1 如果單例注冊表包含Bean,則直接返回該Bean if(SINGLETONS.containsSingleton(beanName)) { return SINGLETONS.getSingleton(beanName); } //3.2單例注冊表不包含該Bean, //則創(chuàng)建并注冊到單例注冊表,從而緩存 SINGLETONS.registerSingleton(beanName, createBean(bd)); return SINGLETONS.getSingleton(beanName); } //4.如果是原型Bean定義,則直接返回根據(jù)Bean定義創(chuàng)建的新Bean, //每次都是新的,無緩存 if(bd.getScope() == BeanDefinition.SCOPE_PROTOTYPE) { return createBean(bd); } //5.其他情況錯誤的Bean定義 throw new RuntimeException("錯誤的Bean定義"); } public void registerBeanDefinition(BeanDefinition bd) { DEFINITIONS.registerBeanDefinition(bd.getId(), bd); } private Object createBean(BeanDefinition bd) { //根據(jù)Bean定義創(chuàng)建Bean try { Class clazz = Class.forName(bd.getClazz()); //通過反射使用無參數(shù)構造器創(chuàng)建Bean return clazz.getConstructor().newInstance(); } catch (ClassNotFoundException e) { throw new RuntimeException("沒有找到Bean[" + bd.getId() + "]類"); } catch (Exception e) { throw new RuntimeException("創(chuàng)建Bean[" + bd.getId() + "]失敗"); } }

其中方法getBean用于獲取根據(jù)beanName對于的Bean定義創(chuàng)建的對象,有單例和原型兩類Bean;registerBeanDefinition方法用于注冊Bean定義,私有方法createBean用于根據(jù)Bean定義中的類型信息創(chuàng)建Bean。

?

3)測試一下吧,在此我們只測試原型作用域Bean,對于每次從Bean工廠中獲取的Bean都是一個全新的對象,代碼片段(BeanFatoryTest)如下:

@Test public void testPrototype () throws Exception { //1.創(chuàng)建Bean工廠 DefaultBeanFactory bf = new DefaultBeanFactory(); //2.創(chuàng)建原型 Bean定義 BeanDefinition bd = new BeanDefinition(); bd.setId("bean"); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.setClazz(HelloImpl2.class.getName()); bf.registerBeanDefinition(bd); //對于原型Bean每次應該返回一個全新的Bean System.out.println(bf.getBean("bean") != bf.getBean("bean")); }

最后讓我們看看如何在Spring中進行配置吧,只需指定<bean>標簽屬性“scope”屬性為“prototype”即可:

<bean class="cn.javass.spring.chapter3.bean.Printer" scope="prototype"/>

?Spring管理原型對象在Spring容器中存儲如圖3-6所示,Spring不會緩存原型對象,而是根據(jù)Bean定義每次請求返回一個全新的Bean:

圖3-6 原型處理

?????? 單例和原型作用域我們已經(jīng)講完,接下來讓我們學習一些在Web應用中有哪些作用域:

?

3.4.2? Web應用中的作用域

在Web應用中,我們可能需要將數(shù)據(jù)存儲到request、session、global?session。因此Spring提供了三種Web作用域:request、session、globalSession。

?

一、request作用域:表示每個請求需要容器創(chuàng)建一個全新Bean比如提交表單的數(shù)據(jù)必須是對每次請求新建一個Bean來保持這些表單數(shù)據(jù),請求結束釋放這些數(shù)據(jù)。

?

二、session作用域:表示每個會話需要容器創(chuàng)建一個全新Bean比如對于每個用戶一般會有一個會話,該用戶的用戶信息需要存儲到會話中,此時可以將該Bean配置為web作用域。

?

三、globalSession類似于session作用域,只是其用于portlet環(huán)境的web應用。如果在非portlet環(huán)境將視為session作用域。

?

配置方式和基本的作用域相同,只是必須要有web環(huán)境支持,并配置相應的容器監(jiān)聽器或攔截器從而能應用這些作用域,我們會在集成web時講解具體使用,大家只需要知道有這些作用域就可以了。

?

3.4.4?自定義作用域

在日常程序開發(fā)中,幾乎用不到自定義作用域,除非又必要才進行自定義作用域。

首先讓我們看下Scope接口吧:

package org.springframework.beans.factory.config; import org.springframework.beans.factory.ObjectFactory; public interface Scope { Object get(String name, ObjectFactory<?> objectFactory); Object remove(String name); void registerDestructionCallback(String name, Runnable callback); Object resolveContextualObject(String key); String getConversationId(); }

?

1)Object get(String name, ObjectFactory<?> objectFactory)用于從作用域中獲取Bean,其中參數(shù)objectFactory是當在當前作用域沒找到合適Bean時使用它創(chuàng)建一個新的Bean;

?

2)void registerDestructionCallback(String name, Runnable callback)用于注冊銷毀回調,如果想要銷毀相應的對象則由Spring容器注冊相應的銷毀回調,而由自定義作用域選擇是不是要銷毀相應的對象;

?

3)Object resolveContextualObject(String key)用于解析相應的上下文數(shù)據(jù),比如request作用域將返回request中的屬性。

?

4)String getConversationId()作用域的會話標識,比如session作用域將是sessionId。

package cn.javass.spring.chapter3; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; public class ThreadScope implements Scope { private final ThreadLocal<Map<String, Object>> THREAD_SCOPE = new ThreadLocal<Map<String, Object>>() { protected Map<String, Object> initialValue() { //用于存放線程相關Bean return new HashMap<String, Object>(); } };

?讓我們來實現(xiàn)個簡單的thread作用域,該作用域內創(chuàng)建的對象將綁定到ThreadLocal內。

@Override public Object get(String name, ObjectFactory<?> objectFactory) { //如果當前線程已經(jīng)綁定了相應Bean,直接返回 if(THREAD_SCOPE.get().containsKey(name)) { return THREAD_SCOPE.get().get(name); } //使用objectFactory創(chuàng)建Bean并綁定到當前線程上 THREAD_SCOPE.get().put(name, objectFactory.getObject()); return THREAD_SCOPE.get().get(name); } @Override public String getConversationId() { return null; } @Override public void registerDestructionCallback(String name, Runnable callback) { //此處不實現(xiàn)就代表類似proytotype,容器返回給用戶后就不管了 } @Override public Object remove(String name) { return THREAD_SCOPE.get().remove(name); } @Override public Object resolveContextualObject(String key) { return null; } }

Scope已經(jīng)實現(xiàn)了,讓我們將其注冊到Spring容器,使其發(fā)揮作用:

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map><entry> <!-- 指定scope關鍵字 --><key><value>thread</value></key> <!-- scope實現(xiàn) --> <bean class="cn.javass.spring.chapter3.ThreadScope"/> </entry></map> </property> </bean>

通過CustomScopeConfigurer的scopes屬性注冊自定義作用域實現(xiàn),在此需要指定使用作用域的關鍵字“thread”,并指定自定義作用域實現(xiàn)。來讓我們來定義一個“thread”作用域的Bean,配置(chapter3/threadScope.xml)如下:

<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl" scope="thread"/>

最后測試(cn.javass.spring.chapter3.ThreadScopeTest)一下吧,首先在一個線程中測試,在同一線程中獲取的Bean應該是一樣的;再讓我們開啟兩個線程,然后應該這兩個線程創(chuàng)建的Bean是不一樣:

?

?

自定義作用域實現(xiàn)其實是非常簡單的,其實復雜的是如果需要銷毀Bean,自定義作用域如何正確的銷毀Bean。

總結

以上是生活随笔為你收集整理的DI 之 3.4 Bean的作用域(捌)的全部內容,希望文章能夠幫你解決所遇到的問題。

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