spring的核心兩個東西:依賴注入(Dependency Injection,DI)和麪向切面編程(Aspect-Oriented Programming,AOP)。java
1、DI功能是如何實現的spring
任何一個有實際應用的程序(確定比helloword複雜)都會有兩個或者更多的類組成。傳統作法就是每一個對象負責管理與本身相互協做的對象的引用,這會致使express
高度耦合和難以測試。編程
public class RescueKnight implements Knight{ private RescueQuest quest; public RescueKnight(){ this.quest = new RescueQuest();--->高度耦合 } public void excuteQuest(){ quest.excute(); } }
這樣一個類,就有很大的侷限性,RescueKnight就只能執行rescue任務,不能執行其餘任務,好比殺一隻巨龍。測試
public class BraveKnight implements Knight{ private Quest quest; public BraveKnight(Quest quest){ this.quest=quest; } public void excuteQuest(){ quest.excute(); } }
這個類就厲害了,它經過構造器傳入一個Quest類型的任務,這個騎士除了很brave之外,還能執行救援,殺龍等任務。這就是DI實現的最大收益,鬆耦合。若是一個對象只經過接口來代表依賴,而不是具體的實現,那麼就能在絕不知情的狀況下用具體實現進行替換。this
bean裝配spa
spring有多種bean裝配方式,xml文件是比較常見的例子日誌
<bean id="knight" class="com.qunhe.hengli.springtest.BraveKnight"> <constructor-arg ref="quest"/> </bean> <bean id="quest" class="com.qunhe.hengli.springtest.KillDragonQuest"> <constructor-arg /> </bean>
等同於java代碼code
@Configuration public void KnightConfig{ @Bean public Knight knight(){ return new BraveKnight(quest()); } @Bean public Quest quest(){ return new KillDragonQuest(); } }
再看上面代碼,儘管BraveKnight依賴於Quest,可是並不知道這個Quest傳過來的是什麼類型的參數,也不知道來自哪裏。你只有經過xml配置才知道這些組件這麼裝配xml
起來,這樣也就能在不改變依賴的狀況下修改依賴。實現代碼的時候你只要加載xml配置文件,就能得到對象的引用。
方法的調用
public class KnightMain { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(".xml"); Knight knight = context.getBean(Knight.class); knight.excuteQuest(); } }
這個類徹底不知道這個騎士執行了什麼任務,且不知道是哪一個騎士來執行的。
2、面向切面編程
面向切面編程是爲了促使軟件系統實現關注點的一項技術。好比一個系統由多個組件組成,各個組件負責一塊特定的功能,可是每每它須要負責功能以外的一些事情。好比打日誌功能,這些常常要融入到有核心業務的組件裏面去,若是在有核心業務的組件裏面去實現打日誌功能那麼代碼會變的很混亂,切若是打日誌代碼的邏輯須要修改,那麼須要到各個組件中修改,即便把打日誌寫成一個模塊,各個組件裏面仍是會調用這個模塊的方法。這樣組件不只僅關注本身的核心功能,還須要知道各個對象須要記日誌,還要親自執行這些功能。
總之使用AOP能使這些組件具備更高的內聚性,更關注本身的核心業務,不須要涉及系統服務所帶來的複雜性。
好比咱們須要一個詩人來對騎士進行歌頌,這是一個記載騎士事蹟的服務類。
public class Poet { public Poet(){ } public void beforeQuest(){ System.out.println("騎士你太勇敢了!"); } public void afterQuest(){ System.out.println("騎士你在這次任務中表現的真出色!"); } }
程序中poet類只有兩個方法,一個是在騎士執行任務以前調用的,一個是在執行以後。下面讓一個knight可使用poet:
public class BraveKnight implements Knight{ private Quest quest; private Poet poet; public BraveKnight(Quest quest,Poet poet){ this.quest=quest; this.poet=poet; } public void excuteQuest(){ poet.beforeQuest(); quest.excute(); poet.afterQuest(); } }
而後咱們只要把poet這個bean注入到knight中就好了,可是爲何詩人的事須要騎士去提醒他作?應該要他自主來完成啊。而且knight的代碼變複雜了,並且若是一個knight不須要poet來歌頌事蹟呢?
<bean id="knight" class="com.qunhe.hengli.springtest.BraveKnight"> <constructor-arg ref="quest"/> </bean> <bean id="quest" class="com.qunhe.hengli.springtest.KillDragonQuest"> <constructor-arg /> </bean> <bean id="poet" class="com.qunhe.hengli.springtest.Poet"> <constructor-arg /> </bean> <aop:config> <aop:aspect ref="poet"> <aop:pointcut id="excuteQuest" expression="execution(* com.qunhe.hengli.springtest.Knight.excuteQuest( . . ))
and within(com.qunhe.hengli.springtest.*)) "/>--定義切點 <aop:before pointcut-ref="excuteQuest"--聲明前置通知 method="beforeQuest"/> <aop:after pointcut-ref="excuteQuest"--聲明後置通知 method="afterQuest"/> </aop:aspect> </aop:config>
首先聲明瞭一個bean在<aop:aspect ref="poet">引用這個bean,<aop:before/>聲明瞭一個前置通知,<aop:after/>聲明瞭一個後置通知,在<aop:pointcut/>
的expression裏選擇要應用通知的位置,語法用的是aspectj的切點表達式語言。
execution指示器
1.第一個*表示返回值爲任意值,咱們不用關心方法的返回值
2.com.qunhe.hengli.springtest表示切點僅匹配springtest這個包,能夠寫*表示匹配任意的包,就不用關心在哪一個包下了
3. .Knight表示匹配Knight這個接口,就不用關心類名了
4. .excuteQuest( . . )指定了方法,而且(. .),表示咱們不用關心方法傳入的參數類型
and表示切點須要匹配全部指示器,固然也能夠用or,not
within指示器
within(com.qunhe.hengli.springtest.*)表示在springtest包下的任意方法被調用時