寒冷的冬天,一周兩節課,掏出買了一年沒翻過的《Spring實戰》。
剛剛接觸spring的我對它還不是很熟悉,對各種知識的認知也比較淺薄,但我會學習的過程通過隨筆記錄下來,監督自己學下去。
1 依賴注入(DI)
大部分的Spring的新手(我)在學習之初對依賴注入這個詞感到迷茫,事實上它並沒有那么復雜,應用依賴注入會使得代碼變得更簡單、更容易理解。
通常,我們開發的java應用都是由多個類組成,它們之間相互協作來完成特定的業務邏輯。每個對象之間相互聯系,導致高度耦合的代碼。
參考代碼:
package com.spring; public class Performer { private Violin violin; public Performer(){ violin=new Violin(); //與Violin緊密耦合 } public void play(){ violin.play(); } } class Violin extends Instrument { public void play() { System.out.println("Violin music!"); } } class Instrument { void play(){}; }
上面的代碼有個非常明顯的問題:Performer在構造函數中創建Violin,這使得Performer與Violin緊密耦合在一起,並且當演奏家需要演奏其他樂器時,就需要改寫代碼。
參考代碼:
package com.spring; public class Performer { private Instrument ins; public Performer(Instrument ins){ this.ins=ins; } public void play(){ ins.play(); } } class Violin extends Instrument { public void play() { System.out.println("Violin music!"); } } class Instrument { void play(){}; }
不同於之前的演奏家,這次的演奏家沒有創建樂器,而是通過構造函數將樂器通過構造參數傳入。這便是依賴注入的一種:構造器注入。
它不僅能夠演奏小提琴,無論是鋼琴、大提琴、手風琴等繼承了Instrument的子類都能作為參賽傳入。
而且它本身並不知道將會演奏什么樂器,這與它無關。這便是依賴注入的好處-------松耦合。
現在performer可以接受任意instrument,那我們如何將instrument傳遞給它呢(裝配)?Spring有多種裝配的方式,XML配置是最常用的一種。
在classpath下創建xml文件:
<?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-3.0.xsd"> <bean id="performer" class="com.spring.Performer"> <constructor-arg ref="violin"/> </bean> <bean id="violin" class="com.spring.Violin"></bean> </beans>
測試是否成功:
package com.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PerformerMain { public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext apc = new ClassPathXmlApplicationContext("spring.xml"); Performer hello = (Performer) apc.getBean("performer"); hello.play(); } }
2 面向切面編程(AOP)
AOP:允許你把遍布應用各處的功能分離出來形成可重用的組件。
比方說,系統中的日志、事務管理。安全服務等,通常會分散到你的每一個組件中,哪怕只是調用某個方法,但他依然會使你的代碼變得混亂並且不易修改。某個組件應該只關心如何實現自身的業務邏輯,與其無關的代碼(日志,安全等)應該少出現甚至不出現。
AOP:
AOP使得這些組件具有更高的內聚性以及更加關注與自身業務,完全不需要涉及其他系統服務,甚至你的核心業務根本不知道它們(日志模塊,安全模塊)的存在。
為了了解Spring中如何使用切面,我依然使用上面的列子。
我們現在需要記錄每次演奏開始的時間與結束的時間,通常我們會這么做:
package com.spring; import java.text.SimpleDateFormat; import java.util.Date; public class Performer { private Instrument ins; private Record rec; public Performer(Instrument ins){ this.ins=ins; this.rec=new Record(); } public void play(){ rec.starttime(); ins.play(); rec.endtime(); } } class Record{ private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public void starttime(){ System.out.println(df.format(new Date())); } public void endtime(){ System.out.println(df.format(new Date())); } }
從上面的代碼我們可以明顯的看出,performer應該專心演奏,而不需要去做記錄時間這種事情,這使得Performer的代碼復雜化。
如何將Record抽象為切面呢?只需要在配置文件中聲明就可以了:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" 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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" default-autowire="byName"> <bean id="performer" class="com.spring.Performer"> <constructor-arg ref="violin" /> </bean> <bean id="violin" class="com.spring.Violin"></bean> <bean id="record" class="com.spring.Record"></bean> <aop:config> <aop:aspect ref="record"> <aop:pointcut expression="execution(* com.spring.Performer.play(..))" id="play"/> <aop:before method="starttime" pointcut-ref="play"/> <aop:after method="endtime" pointcut-ref="play"/> </aop:aspect> </aop:config> </beans>
package com.spring; import java.text.SimpleDateFormat; import java.util.Date; public class Performer { private Instrument ins; public Performer(Instrument ins){ this.ins=ins; //與Violin緊密耦合 } public void play(){ ins.play(); } } class Record{ private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public void starttime(){ System.out.println(df.format(new Date())); } public void endtime(){ System.out.println(df.format(new Date())); } }
運行結果:
注意:aop除了spring的包外還需要aspectJ框架的包: