Spring:
簡單的JavaBean實現EJB才能完成的事情
解決企業級應用開發的複雜性創建,簡化JAVA開發
- 基於POJO的輕量級和最小侵入性coding
- 通過DI AOP
- 基於切面和慣例進行聲明式coding
- 透過切面和模板減少樣板式coding
- DI (Dependency Injection): 關係注入,保持low coupling
- 通過DI,對象的依賴關係將由系統負責協調對象的第三方組建,在創建對象的時候進行設定,無需自行創建或管理依賴關係
- 在runtime的時候賦予他們所依賴的對象
- 通常會透過interface注入對象,如此能確保low coupling
- constructor injection: 在constructor內將interface(原本要使用的object implements的interface)當作parameter注入,而不是在constructor內自行new object。好處是只要implements此interface,具體是哪一個object就不重要了。這就是DI所帶來最大的好處loose coupling
- 使用interface表明依賴關係
- 依賴能夠使用不同的具體object進行替換
- 建造應用組建之間的協作行為通常稱為wiring
- 使用XML配置,需要DI需要的class需要被聲明為bean
-
<bean id="knight" class="com.spring.knights.BraveKnight"> <constructor-arg ref="quest" /> </bean> // BraveKnight class需要有一個實作Quest的parameter <bean id="quest" class="com.spring.knights.SlayDragonQuest"> <constructor-arg value="#{T(System).out}" /> </bean> // BSlayDragonQuest實作了Quest並且傳入BraveKnight,且傳入了#{T(System).out}當作parameter進入SlayDragonQuestconstructor
- 使用ClassPathXmlApplicationContext加載Knight的Spring application context(此class將會加載路徑下的一個或多個XML配置文件),在main內可做加載。
public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "META-INF/spring/knight.xml"); // 加載 Knight knight = context.getBean(Knight.class); // 編譯器不會知道是哪個 knight執行哪個quest,只有knights.xml知道 knight.embarkOnQuest(); context.close(); }
- 注意這個class完全不知道是誰
- 使用Java描述配置(與XML功能相同)
@Configuration public class KnightConfig { @Bean public Knight kngiht() { // Knight interface return new BraveKnight(quest()); } @Bean public Quest quest() {// Quest interface return new SlayDragonQuest(System.out); } }
- 上述的好處是,BraveKnight雖然依賴Quest,但並不知道真正傳入的是哪一個實作Quest的Object。相同,SlayDragonQuest雖然依賴PrintStream,但也不需要了解這個PrintStream是什麼樣子
- 結論:可以在不改變依賴的class的情況下修改依賴關係
- 藉由Spring通過配置,才能了解這些組成部分如何整合
- AOP (Aspect-Oriented Programming) 面向切面 high cohesion
- 允許把應用各處的功能分離,形成可重用的組件
- 將散落各處的logic匯集於一處(aspect)
- runtime的時候能夠把bean wire在一起,這樣就能夠有效地賦予bean新的行為
- 問題
- 一系統有學生module、課程module、講師module
- 有一些common module: log、security、事務管理的服務
- 每個module都要使用到這些服務
- 這些服務的調用散布到服務中,但這些服務卻不是module的核心業務,會導致系統變得複雜
- AOP能夠讓服務模組化 ,以聲明的方式將這些服務應用到需要的module上。
- 結果能夠使得module有high cohesion,且能夠更關注在自身的業務邏輯上,完全不需要了解使用服務時所帶來的複雜性
- AOP能夠確保POJO的簡單性
- 範例如下:假設Minstrel在作戰前後BraveKnight要唱歌
public class Minstrel { private PrintStream stream; public Minstrel(PrintStream stream) { this.stream = stream; } public void singBeforeQuest() { stream.println("Fa la la, the knight is so brave!"); } public void singAfterQuest() { stream.println("Tee hee hee, the brave knight " + "did embark on a quest!"); } }
- 如此,在BraveKnight class內可能一開始會這樣寫
public class BraveKnight implements Knight { private Quest quest; private Minstrel minstrel; public BraveKnight( Quest quest, Minstrel minstrel) { this.quest = quest; this.minstrel = minstrel; } public void embarkOnQuest() { minstrel.singBeforeQuest(); // 開戰前唱歌 quest.embark(); minstrel.singBeforeQuest(); // 開戰後唱歌 } }
- 然後將minstrel bean的配置injects到BraveKnight constructor中。
- 但是,這會繁衍出一些問題:
- BraveKnight必須去管理Minstrel唱歌。而吟遊詩人唱歌應該份內的事情,不應該由BraveKnight提醒。
- BraveKnight必須將Minstrel注入,導致BraveKnight代碼複雜化
- 是否需要再寫一個不需要Minstrel的BraveKnight? 若Minstrel為null時,還得需要引入空值?
- 使用AOP可以聲明Minstrel必須要唱歌,BraveKnight本身不需要直接使用Minstrel的方法
- AOP實例
<bean id="knight" class="sia.knights.BraveKnight"> <constructor-arg ref="quest" /> </bean> <bean id="quest" class="sia.knights.SlayDragonQuest"> <constructor-arg value="#{T(System).out}" /> </bean> <bean id="minstrel" class="sia.knights.Minstrel"> //先聲明為bean <constructor-arg value="#{T(System).out}" /> </bean> <aop:config> <aop:aspect ref="minstrel"> <aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/>//定義切點 <aop:before pointcut-ref="embark" method="singBeforeQuest"/> // 前置通知before advice,引用了embark的method <aop:after pointcut-ref="embark" method="singAfterQuest"/>// 後置通知after advice,引用了embark的method </aop:aspect> </aop:config>
- 即使Mistrel變成Spring aspect,但仍然是一個POJO,並不會修改到原本的代碼
- Minstrel可以被應用到BraveKnight,且BraveKnight不需要invoke Minstrel的method,實際上,BraveKnight也不知道Minstrel的存在
- 使用模版消除樣板式代碼 boilerplate code
- 為了實現通用的及簡單的任務,不得不一遍遍地重複代碼
- 例如JDBC
- create connection
- create statement
- do query
- catch SQL exception
- close connection
- catch ...
- 等等這些使用JDBC都需要用到的code,就是JDBC boilerplate code
- Spring通過模版encapsulation來消除boilerplate code
沒有留言:
張貼留言