第四章:重新來認識你的老朋友Spring框架
版權宣告:本文為博主原創文章,轉載需註明出處,最新資訊請關注個人主頁http://www.liangdongzhen.cn https://blog.csdn.net/ldz1997106/article/details/82865523
歡迎檢視 ofollow,noindex" target="_blank">Java開發之上帝之眼系列教程 ,如果您正在為Java後端龐大的體系所困擾,如果您正在為各種繁出不窮的技術和各種框架所迷茫,那麼本系列文章將帶您窺探Java龐大的體系。本系列教程希望您能站在上帝的角度去觀察(瞭解)Java體系。使Java的各種後端技術在你心中模組化;讓你在工作中能將Java各個技術瞭然於心;能夠即插即用。本章我們來一起了解你的老朋友Spring框架。
Spring這個詞對於開發者想必不會陌生,可能你每天都在使用Spring,享受著Spring生態提供的服務,理所當然的用著SpringIOC/">IOC和SpringAOP去實現老闆交給你的功能 ,唔 它就是這樣使用的(類宣告為Bean元件,然後注入),沒錯 能完成老闆任務,沒毛病。如果向你提問什麼是Spring,Spring有什麼核心功能呢,你會想:這太簡單了,Spring就是框架嘛,Spring核心功能就是IOC和AOP,So Easy!!可是你真的瞭解Spring嗎?帶著這個疑問讓我們一起來重新瞭解一下Spring! 本文所有示例原始碼下載
Spring的起源和根本使命
Spring是一個開源框架,最早由RodJohnson建立,是為了解決企業級應用開發的複雜性而建立的。很多框架都宣稱在某些方面針對Java開發做出了簡化,但是Spring的目標是致力於全方位簡化Java開發,這也是Spring的根本使命"簡化Java開發"

Spring如何簡化Java開發
為了降低Java開發的複雜性,Spring採取了以下4種關鍵策略,請務必牢記下面4中策略於心,這樣會幫助你更深的理解Spring。下面我們來逐一解讀每一條策略,你就能明白Spring是如何簡化Java開發的。
- 基於POJO的輕量級和最小侵入性程式設計
- 通過依賴注入和麵向介面實現鬆耦合
- 基於切面和慣例進行宣告式程式設計
- 通過切面和模板減少樣版式程式碼
1.基於POJO的輕量級和最小侵入性程式設計
Spring竭力避免因自身的API而弄亂你的程式碼。Spring不會強迫你實現Spring規範的介面或繼承Spring規範的類。相反,在一個基於Spring構建的應用中,應用中的類沒有任何痕跡表明你使用了Spring。最壞的情況就是你在應用的類中發現了Spring的相關注解,但它依然是POJO,下面我們來看一個在Spring中的一個POJO例子。 example1例項原始碼下載
/** * @Author:jimisun * @Description: * @Date:Created in 20:32 2018-09-26 * @Modified By: */ public class HelloSpringBean { public String sayHello() { return "Hello Spring!!!"; } }
你可以看到,這就是一個POJO(簡單的JAVA類),沒有任何地方表明它是Spring元件,Spring非侵入式程式設計模型意味著這個類在Spring應用和非Spring應用中都可以發揮同樣的作用。儘管看起來很簡單;但Spring通過IOC(Inversion of Control)管理這個POJO,然後通過DI(Dependency Inject)來注入他們,這個POJO就變的魔力十足;那麼DI是如何幫助應用物件彼此之間保持鬆耦合的呢?
2.通過依賴注入和麵向介面實現鬆耦合
來我們一起來看一下一個簡單的POJO example2例項原始碼下載
/** * @Author:jimisun * @Description: * @Date:Created in 07:44 2018-09-27 * @Modified By: */ public class BeautifulGirl implements Gril { private EatAction eat; public BeautifulGirl() { this.eat = new EatAction(); } @Override public void action() { eat.action(); } }
在BeautifulGirl(可愛的小女孩)這個類中,在構造方法中建立一個EatAction(吃飯動作)。這樣就極大限制了BeautifulGirl(可愛的小女孩)的能力;如果現在小女孩需要去玩耍呢?或者需要去睡覺呢?真是太抱歉了,BeautifulGirl(可愛的小女孩)只會吃東西這個動作。這是什麼原因呢?這就是BeautifulGirl(可愛的小女孩)和EatAction(吃飯動作)這兩個類緊緊耦合在了一起!緊密耦合同時會造成程式碼的難以測試,難以服用,難以理解,並且典型地表現出"打地鼠“式的Bug特性(修復一個Bug,將會出現一個或多個新Bug),所以我們可以知道耦合是必須的,但必須謹慎管理耦合,但是怎麼才算是謹慎處理耦合關係呢?

上圖所示,DI(依賴注入) 會將所依賴關係自動交給目標物件,而不是讓物件本身建立所依賴物件 。物件的依賴關係將由系統中負責協調個物件依賴關係的第三方元件(類似Spring)在建立物件的時候設定。現在我們明白了通過DI依賴注入可以將物件之間依賴關係輕鬆解耦,那麼我們來看一下簡單的例項吧。 example3例項原始碼下載
/** * @Author:jimisun * @Description: * @Date:Created in 07:44 2018-09-27 * @Modified By: */ public class BeautifulGirl implements Gril { private Action action; public BeautifulGirl(Action action) { this.action = action; } @Override public void action() { action.action(); } }
從上面例項程式碼中可以看到BeautifulGirl本身並沒有建立任何的動作,而是通過構造方法傳入一個實現了Action(動作)介面的實現類即可,也就是說BeautifulGirl可以完成任意實現了Action介面的動作(睡覺啦…玩耍啦…旅行啦…)。
這裡的要點是BeautifulGirl沒有與任何特定的Action發生耦合。BeautifulGirl只需要的是一個實現Action介面就行,物件本身只是通過介面(而非具體實現或初始化過程)來表明依賴關係,那麼這種依賴就能夠在BeautifulGirl不知情的情況下替換不同的具體動作。好了我們現在明白了DI進行依關係解耦的原理了,下面我們看一下如何在Spring中應用DI。 example4例項原始碼下載
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean class="com.jimisun.spring.example4.BeautifulGirl" id="beautifulGirl"> <constructor-arg ref="action"/> </bean> <bean class="com.jimisun.spring.example4.SleepAction" id="action"></bean> </beans>
/** * @Author:jimisun * @Description: * @Date:Created in 07:53 2018-09-27 * @Modified By: */ public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml"); BeautifulGirl beautifulGirl = (BeautifulGirl) context.getBean("beautifulGirl"); beautifulGirl.action(); context.close(); } }
這樣執行Main方法,從Context中獲取BeautifulGirl例項執行action方法。當然Spring提供了基於Java的配置,可作為XML配置檔案的代替方案 example5例項原始碼下載
/** * @Author:jimisun * @Description: * @Date:Created in 08:40 2018-09-27 * @Modified By: */ @Configuration public class SpringConfig { @Bean public SleepAction sleepAction() { return new SleepAction(); } @Bean public BeautifulGirl beautifulGirl() { return new BeautifulGirl(sleepAction()); } }
只不過Spring的上下文的實現應該使用AnnotationConfigApplicationContext進行載入配置
/** * @Author:jimisun * @Description: * @Date:Created in 07:53 2018-09-27 * @Modified By: */ public class Main { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class); SleepAction action = applicationContext.getBean(SleepAction.class); action.action(); } }
3.基於切面和慣例進行宣告式程式設計
現在我們已經明白DI能夠將互相協作的軟體保持鬆耦合,而面向切面程式設計(aspect-oriented-programming,AOP)可以將遍佈應用各處的功能分離出來形成可從用的元件。
我們仍然使用上面的例子,BeautifulGirl隨心所欲的執行各種動作真的沒問題嗎?如果可愛的小女孩執行的動作是去河邊玩耍,吃一些危險的東西呢?那麼我們勢必需要一個家長(Parent類)進行監督小女孩的動作是否是安全的,下面我們來看一下Parent類。 example6例項原始碼下載
/** * @Author:jimisun * @Description: * @Date:Created in 09:32 2018-09-27 * @Modified By: */ public class Parent { public void check() { System.out.println("檢查動作是否安全......."); } }
非常簡單!Parent(家長類)只有一個方法就是check,那麼現在就讓Parent對BeautifulGirl的執行動作進行檢查吧。
<bean class="com.jimisun.spring.example6.BeautifulGirl" id="beautifulGirl"> <constructor-arg ref="action"/> <constructor-arg ref="parent"/> </bean> <bean class="com.jimisun.spring.example6.SleepAction" id="action"></bean> <bean class="com.jimisun.spring.example6.Parent" id="parent"></bean> </beans>
/** * @Author:jimisun * @Description: * @Date:Created in 07:44 2018-09-27 * @Modified By: */ public class BeautifulGirl implements Girl { private Action action; private Parent parent; public BeautifulGirl(Action action, Parent parent) { this.action = action; this.parent = parent; } @Override public void action() { parent.check(); action.action(); } }
我們非常聰明,使用了DI將Parent類注入到了BeautifulGirl類中,在BeautifulGirl執行action動作之前執行 parent.check()方法,這樣我們要的效果就達到了。 等等…好像有什麼不對的地方?
- 管理Parent家長的check動作真的是美麗的小女孩的職責嗎?
- 將Parent家長注入到美麗的小女孩類中不是將程式碼複雜化了嗎?
- 我們需不需要一個不需要家長注入的美麗的小女孩呢?
- 如果注入的Parent為NULL我們是否應該在美麗的小女孩中進行校驗呢?
真的是太糟糕了,注入一個Parent(家長)將會產生那麼多的問題,為什麼呢?因為 Parent類的業務於BeautifulGirl無關;BeautifulGirl將會承擔Parent類的職責。
在一個專案中,很多系統服務都會分佈到各個業務模組中,這使得業務模組的邏輯變得十分複雜,而使用AOP能夠使這些服務模組化,並以宣告式的方式將他們應用到他們所需要影響的元件中,使得這些元件將會具有更高的內聚性並且更加關注自身的業務邏輯,完全不需要了解系統服務所帶來的複雜性。
下面我們使用SpringAOP對上面程式碼進行改造 example7例項原始碼下載
<!--宣告Bean--> <bean class="com.jimisun.spring.example7.Parent" id="parent"></bean> <!--宣告切面--> <aop:config> <aop:aspect ref="parent"> <aop:pointcut id="girlAction" expression="execution(* com.jimisun.spring.example7.Action.*(..))"/> <aop:before pointcut-ref="girlAction" method="check"/> </aop:aspect> </aop:config>
/** * @Author:jimisun * @Description: * @Date:Created in 07:44 2018-09-27 * @Modified By: */ public class BeautifulGirl implements Girl { private Action action; public BeautifulGirl(Action action) { this.action = action; } @Override public void girlAction() { action.action(); } }
我們只需要在Spring配置檔案中宣告Parent為Spring Bean,然後將配置在SpringAOP中即可,我 們可以發現BeautifulGirl類中沒有發現任何關於Parent的資訊 ,這就是AOP的魅力所在—— 以宣告的方式將元件應用到他們所需要影響的元件中!
4.通過切面和模板減少樣版式程式碼
在通過JavaAPI進行程式設計,我們經常會編寫很多重複的程式碼例如JDBC訪問資料庫,JMS,JNDI等場景下會重複編寫大量的樣版式程式碼,而Spring皆在通過模板封裝來消除模板式程式碼,例如Spring針對JDBC訪問資料庫進行封裝的JdbcTemplate等,這裡就是Spring使用封裝的模板來減少JavaAPI樣版式程式碼。 example8例項原始碼下載
/** * @Author:jimisun * @Description: * @Date:Created in 11:13 2018-09-27 * @Modified By: */ public class Main { public static void main(String[] args) { JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.execute("select * from user"); } }
非常感謝大家耐心能看到這裡,在本文中我儘可能地詳細闡述Spring使如何簡化Java開發?遵循哪些策略?以及為什麼遵循的好處。
Java開發之上帝之眼系列教程其他文章
本文資料來源:
勘誤&感謝
本系列文章資料來源很多出自於網際網路和在下本身的見解,受限於個人技術能力水平和其他相關知識的限制,相關見解錯誤或者資料引用錯誤請各位幫助留言校正!引用資料多來自於網際網路,在下在引用前會遵循各位前輩或者博主的引用說明表示感謝,但網際網路資料多是轉發再轉發或存在遺漏請原作者內信聯絡指正。