為什麼使用 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) {
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()));
}
}
(轉自 鄭某某i)