架構在項目中的表現就是一系列的jar包,例如Thymeleaf就是一個架構。
每種架構都會解決某種特定的問題,可能是開發效率的問題,或運作效率的問題,或代碼管理維護的問題等等。
項目中使用架構就相當于得到了一個“毛坯房”,使用了架構之後,開發人員隻需要關心後續的“裝修”即可。
絕大部分的架構都有特定的使用方式,在使用時,必須遵循架構的使用規則!
每個架構都可能是若幹個開發人員甚至開發團隊多年的工作積累的作品,對于初學者來說,不要過于鑽牛角尖,嘗試了解架構的底層實作原理!
簡單的說:使用架構,可以讓程式設計變得更加簡單!在學習架構時,主要學習架構的正确使用方式!
假設在項目中需要開發一個使用者注冊的功能!在項目中可能存在:
在以上代碼中,<code>UserRegServlet</code>就是依賴于<code>UserDao</code>的!
如果某個類過于依賴于另外一個類,通常稱之為了“耦合度較高”,是不利于代碼的管理和維護的,簡單的說,如果<code>UserRegServlet</code>依賴于<code>UserDao</code>,在未來的某一天,<code>UserDao</code>已經不能滿足項目的需求了(可能是因為代碼有Bug,或者使用的技術比較落後等),如果需要把<code>UserDao</code>替換掉,替換難度大就會影響項目的管理和維護,為了解決這樣的問題采取的解決方案就稱之為“解耦”,使得依賴關系不那麼明确,甚至就是不明确!
就以上<code>UserRegServlet</code>依賴<code>UserDao</code>的問題,如果要解耦,可以先建立一個接口:
然後,使得<code>UserDao</code>是實作了以上接口的:
經過以上調整以後,如果在<code>UserRegServlet</code>中需要使用到<code>UserDao</code>,以前的代碼是這樣的:
現在就可以改為:
以上代碼就相當于:
改成這樣以後,在同一個項目中,無論多少個<code>Servlet</code>元件需要使用到<code>UserDao</code>,都可以使用以上“聲明為接口,建立實作類的對象”的文法風格,如果以後<code>UserDao</code>需要被替換掉,也隻需要替換“指派”的代碼,聲明部分是不需要替換的!例如需要把<code>UserDao</code>替換為<code>UserMybatisDao</code>時,原來的代碼是:
新的代碼就可以是:
在後續的使用中,就可以是:
也就是說,在<code>UserDao</code>換成了<code>UserMybatisDao</code>時,在各個<code>Servlet</code>中,都隻需要調整等于号右側的内容,而不再需要修改等于号左側的部分!
當然,關于以上代碼的右側部分,還可以使用“工廠設計模式”作進一步的處理:
當有了工廠後,此前的代碼就可以進一步調整為:
可以發現,以上代碼中不再出現任何一個實作類的名字了,無論是哪個<code>Servlet</code>元件需要通路資料庫,都聲明為以上代碼即可,以後,如果實作類需要被替換,也隻需要替換工廠方法的傳回值即可!
在實際項目開發時,項目中的元件的依賴更加複雜,為每個元件都建立對應的接口及工廠是非常麻煩的,而Spring架構就很好的解決了這個問題,可以簡單的将Spring了解為一個“萬能工廠”,當使用了Spring架構後,就不必自行開發工廠了!
Spring架構的主要作用:解決了建立對象和管理對象的問題。
建立Maven Project,在建立過程中,勾選Create a simple project,Group Id填為<code>cn.tedu</code>,Artifact Id填為<code>spring01</code>,其它項保持預設即可。
使用Spring架構時,必須在項目的pom.xml中添加<code>spring-context</code>的依賴:
首先,在項目中,建立<code>cn.tedu.spring</code>包,并在這個包下建立<code>BeanFactory</code>類:
目前,代碼放在哪個包中并不重要,應該養成習慣,每個類都應該放在某個包中,不要讓任何類不放在任何包中! 以上類的名稱也不重要,是自定義的!
如果希望由Spring來建立并管理某個類的對象,必須在以上類中添加方法,關于這個方法:
應該使用<code>public</code>權限;
傳回值類型就是需要Spring建立并管理的類的對象的類型;
方法名稱可以自定義;
參數清單暫時為空;
在方法體中,自行編寫建立傳回值對象的代碼。
假設需要Spring來建立<code>Date</code>類型的對象,則在類中添加方法:
Spring架構要求:建立對象的方法必須添加<code>@Bean</code>注解,并且,這樣的方法必須在配置類中!任何一個類添加了<code>@Configuration</code>注解都可以作為配置類!
完成後,應該使用一個可以運作的類,或通過單元測試來檢驗“是否可以通過Spring容器擷取對象”。本次先建立一個可以運作的類:
當方法的聲明之前添加了<code>@Bean</code>注解,就表示這個方法是需要由Spring架構所調用,并且,由Spring架構管理該方法傳回的對象的!預設情況下,該方法的名稱就是後續擷取對象時,調用<code>getBean()</code>方法的參數!
由于添加了<code>@Bean</code>注解的方法是被Spring架構調用的,不需要自行編寫代碼來調用這個方法,是以,Spring的建議是“使用合理的屬性名稱作為方法名,并不需要使用動詞或動詞為字首的方法名”,簡單的說,如果方法是為了擷取<code>Date</code>類型的對象的,該方法的名稱應該是<code>date</code>,而不是<code>getDate()</code>,則後續調用<code>getBean()</code>時,參數就是<code>date</code>這個名稱!
當然,如果不遵循Spring的建議,還可以在<code>@Bean</code>注解中配置注解參數來指定Bean的名稱,例如:
則後續就根據注解參數來擷取對象:
其關系如下圖:

其實,在開發項目時,真的不必關心這些問題,也就是說,例如是一個擷取<code>Date</code>對象的方法,其名稱到底是<code>date</code>還是<code>getDate</code>都是正确的!畢竟這個方法最終就是由Spring架構來調用,開發人員不會自行調用該方法!
由Spring管理的對象,預設情況下,是單例的!是以,其作用域就非常久!
在Spring管理對象的情況下,讨論對象的作用域,其本質就是讨論其是否單例!
在建立對象的方法之前,添加<code>@Scope</code>注解,并配置注解參數為<code>prototype</code>,就可以使得該對象不是單例的:
由Spring管理的對象,如果是單例模式的,預設情況下,是餓漢式的!在建立對象的方法之前,添加<code>@Lazy</code>注解,就可以調整為懶漢式的:
一般,在開發項目時,極少調整對象的作用域!
Spring的主要作用:建立對象,管理對象;
如果某個方法是用于給Spring架構建立對象的,這個方法就必須添加<code>@Bean</code>注解;
所有添加了<code>@Bean</code>注解的方法,其所在的類應該添加<code>@Configuration</code>注解,凡添加了<code>@Configuration</code>注解的類稱之為配置類;
預設情況下,由Spring管理的對象是單例的,使用<code>@Scope</code>注解可以将Spring管理的對象調整為“非單例”的;
預設情況下,由Spring管理的單例的對象是是“餓漢式”的,使用<code>@Lazy</code>可以将它們改為“懶漢式”的。
單例模式的特點:在同一時期,某個類的對象一定最多隻有1個!也許會嘗試多次的擷取對象,但是,擷取到的一定是同一個對象!
假設項目中有<code>King</code>類:
很顯然,目前它并不是單例的,因為,可以:
以上代碼就建立了3個<code>King</code>類型的對象!如果要實作單例,首先,就必須限制構造方法的通路,例如:
每個類中都可以有若幹個構造方法,如果某個類沒有顯式的聲明任何構造方法,編譯器就會自動添加1個公有的、無參數的構造方法!如果類中已經聲明任何構造方法,則編譯器不會自動添加構造方法!
由于将構造方法聲明為私有的,則原有的<code>King k1 = new King();</code>這類代碼就不能用于建立對象了!
限制構造方法的通路,其目的是“不允許随意建立對象”,并不是“不允許建立對象”,在<code>King</code>類的内部,還是可以建立對象的,可以添加方法,傳回内部建立的對象:
是以,當需要<code>King</code>類型的對象時,可以通過<code>getInstance()</code>方法來擷取!
但是,以上代碼是不可行的!因為,如果要調用<code>getInstance()</code>方法,必須先擷取<code>King</code>的對象,而擷取<code>King</code>對象的唯一方式就是調用<code>getInstance()</code>方法!為了解決這個問題,必須在<code>getInstance()</code>方法的聲明之前添加<code>static</code>修飾符,最終,就可以通過<code>類名.方法名()</code>的文法格式來調用方法了!同時,由于“被<code>static</code>修飾的成員,不可以通路其它未被<code>static</code>修飾的成員”,是以,全局屬性<code>king</code>也必須被<code>static</code>修飾:
至此,基本的單例模式的代碼就設計完成了!
以上代碼是“餓漢式”的單例模式,另外,還有“懶漢式”的單例模式!
基本的懶漢式單例模式的代碼是:
注意:以上代碼是多線程不安全的!
在開發領域中,隻要資料的産生、變化不是開發人員預期的,就稱之為“不安全”,也就是“資料安全問題”。
為了保障線程安全,應該為以上建立對象的代碼片斷“加鎖”,例如:
當然,無論是哪個線程在什麼時候執行以上代碼,都必須先“鎖住”代碼片斷後才能開始執行,是沒有必要的,“鎖”的性能消耗是浪費的,是以,可以進一步調整為:
至此,懶漢式的單例模式就完成了!