Java之SSM中bean相關知識匯總案例講解
bean 的生命周期
對象創建
- 實例化Bean對象,默認選擇無參構造方法,如果隻有一個有參構造那麼調用有參構造,如果隻有多個有參構造那麼報錯,除非其中一個有參構造添加瞭@AutoWired註解;
- 設置Bean的屬性;
- 依賴註入以及判斷是否實現瞭Aware相關接口(BeanNameAware, BeanFactoryAware, ApplicationContextAware)
- 如果這個 Bean 關聯瞭 BeanPostProcessor 接口,將會調用BeanPostProcessor.postProcessorBeforeInitialization()
- 判斷是否實現瞭InitalizingBean接口,實現瞭就執行 InitalizingBean.afterPropertiesSet() 方法
- 如果Bean在配置文件中的定義包含init-method屬性(或者添加瞭@PostConstruct註解),執行指定的方法;
- 執行方法BeanPostProcessor.postProcessorAfterInitialization():例如判斷有沒有實現AOP
- 創建對象完畢;
對象銷毀
- 當要銷毀Bean的時候,如果Bean實現瞭DisposableBean接口,執行destroy()方法。
- 當要銷毀Bean的時候,如果Bean在配置文件中的定義包含destroy-method屬性(或者添加瞭@PreDestroy註解),執行指定的方法
- 銷毀對象完畢
Bean 的作用域
spring 支持 5 種作用域,如下:
request:每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效。
- singleton::單例模式,Spring IoC 容器中隻會存在一個共享的 Bean 實例,無論有多少個Bean 引用它,始終指向同一對象。該模式在多線程下是不安全的。Singleton 作用域是Spring 中的默認作用域
- prototype:每次通過 Spring 容器獲取 prototype 定義的 bean 時,容器都將創建一個新的 Bean 實例,每個 Bean 實例都有自己的屬性和狀態,而 singleton 全局隻有一個對象。根據經驗,對有狀態的bean使用prototype作用域,而對無狀態的bean使用singleton作用域。
- 用於與數據庫交互的存儲數據的bean等,均是有狀態的bean。
- 而僅僅用於操作其他資源的bean,如service controller,就是無狀態的bean。
- 當然,由於spring使用瞭ThreadLocal進行多線程處理,絕大多數bean都可以聲明為singleton作用域。這是後話。
- session:在一次 Http Session 中,容器會返回該 Bean 的同一實例。而對不同的 Session 請求則會創建新的實例,該 bean 實例僅在當前 Session 內有效。同 Http 請求相同,每一次session 請求創建新的實例,而不同的實例之間不共享屬性,且實例僅在自己的 session 請求內有效,請求結束,則實例將被銷毀。如用戶購物車
- global-session:全局session作用域,僅僅在基於Portlet的Web應用中才有意義,Spring5中已經沒有瞭。Portlet是能夠生成語義代碼(例如HTML)片段的小型Java Web插件。它們基於Portlet容器,可以像Servlet一樣處理HTTP請求。但是與Servlet不同,每個Portlet都有不同的會話。
bean的循環依賴
什麼是循環依賴
一個AService裡面引用瞭BService的一個對象,BService裡面又引用瞭AService的一個對象。
那麼在構造AService的bean的時候,會填充屬性以及註入依賴,那麼就需要註入BService的bean,spring發現BService的bean還沒有創建,又會去構造BService的bean。同理BService的bean又需要AService的bean,這時候因為AService的bean還沒有構建好,所以他也會去創建AService的bean。一直循環
怎麼解決:使用二級緩存
- singletonObjects:第一級緩存,裡面放置的是實例化好的單例對象;這個是一直存在的
- earlySingletonObjects:第二級緩存,裡面存放的是提前曝光的單例對象;就是下面圖中的那個緩存
有什麼問題
如果上述的bean不存在AOP,那麼是沒有什麼問題的,但是如果存在AOP的話,那麼構造出來的bean對象就不是原始對象瞭,而是AOP生成的代理對象。如果還是使用二級緩存的話,那麼B從緩存取的是A的原始對象而不是構造好的A的bean對象
怎麼解決
添加一層緩存,singletonFactories:第三級緩存,裡面存放的是要被實例化的對象的對象工廠。
主要代碼如下:
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); }
分析getSingleton()的整個過程,Spring首先從一級緩存singletonObjects中獲取。如果獲取不到,並且對象正在創建中,就再從二級緩存earlySingletonObjects中獲取。如果還是獲取不到且允許singletonFactories通過getObject()獲取,就從三級緩存singletonFactory.getObject()(三級緩存)獲取,如果獲取到瞭則從singletonFactories中移除,並放入earlySingletonObjects中。其實也就是從三級緩存移動到瞭二級緩存。
總結一下流程:
- A在第一步實例化對象之後將自己提前曝光到singletonFactories中
- 在填充屬性時發現自己依賴對象B,此時就嘗試去get(B),發現B還沒有被create,所以走create流程
- B在填充屬性的時候發現自己依賴瞭對象A,於是嘗試get(A),嘗試一級緩存singletonObjects(肯定沒有,因為A的bean還沒有構造完),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,由於A通過ObjectFactory將自己提前曝光瞭,所以B能夠通過ObjectFactory.getObject拿到A對象
- 通過提前引用,直接創建出A的動態代理對象也就是實例化好的A的bean放到第二個緩存中,這樣B的bean就直接實例化完成進入一級緩存。
- 此時回調到A中,A填充屬性這一步完成瞭繼續往下執行,因為bean是單例的,所以A不會又去調用動態代理再創建一個bean,而是直接從第二個緩存裡拿出實例化好的那個bean出來直接用放進一級緩存中。
到此這篇關於Java之SSM中bean相關知識匯總案例講解的文章就介紹到這瞭,更多相關Java之SSM中bean相關知識內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 關於Java Spring三級緩存和循環依賴的深入理解
- Spring解決循環依賴的方法(三級緩存)
- 關於spring循環依賴問題及解決方案
- SpringBean依賴和三級緩存的案例講解
- 聊聊Spring循環依賴三級緩存是否可以減少為二級緩存的情況