1 依賴注入
通過依賴注入(Dependency Injection, DI),對象的依賴關(guān)系將由系統(tǒng)中負責協(xié)調(diào)各對象的第三方組件在創(chuàng)建對象的時候進行設定。
1.1 實現(xiàn)
對于如下類
package com.junzerg.spring_knight;
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
- BraveKnight沒有自己創(chuàng)建探險任務,而是在構(gòu)造的時候把探險任務作為構(gòu)造器參數(shù)傳入。這是依賴注入的一種方式,構(gòu)造器注入(constructor injection)。
- 傳入的探險類型Quest,是所有的探險任務都必須實現(xiàn)的一個接口。
- BraveKnight沒有與特定的Quest實現(xiàn)發(fā)生耦合,只要求探險任務實現(xiàn)了Quest接口。
可以看出:
DI帶來的最大的好處就是松耦合。如果一個對象只通過接口(而不是具體實現(xiàn)或者初始化過程)來表明依賴關(guān)系,那么這種依賴就能在對象本身毫不知情的情況下,用不同的具體實現(xiàn)進行替換。
1.2 注入
對于#1中的BraveKnight類??梢越邮苋我庖环NQuest的實現(xiàn),例如如下類:
package com.junzerg.spring_knight;
import java.io.PrintStream;
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}
創(chuàng)建了這個類之后,剩下的就是將這個類交給BraveKnight。這就是創(chuàng)建應用組件之間的寫作的行為,被稱為裝配(wiring)。
1.3 裝配
Spring支持兩種裝配的方式。
1.3.1 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.xsd">
<bean id="knight" class="com.junzerg.spring_knight.BraveKnight">
<constructor-arg ref="quest" />
</bean>
<bean id="quest" class="com.junzerg.spring_knight.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>
以上配置功能為:
- 將BraveKnight和SlayDragonQuest聲明為Spring中的bean。
- 在構(gòu)造BraveKnight bean的時候傳入SlayDragonQuest bean的應用,作為構(gòu)造器的參數(shù)。
- 在構(gòu)造SlayDragonQuest bean的時候?qū)ystem.out傳入到構(gòu)造器中。
1.3.2 Java描述配置
package com.junzerg.spring_knight.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.junzerg.spring_knight.BraveKnight;
import com.junzerg.spring_knight.Knight;
import com.junzerg.spring_knight.Quest;
import com.junzerg.spring_knight.SlayDragonQuest;
@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}
作用和用 xml配置的一樣。
1.4 實現(xiàn)
1.4.1 xml配置的實現(xiàn)
對于xml配置的情況,使用ClassPathXmlApplicationContext(加載位于應用程序類路徑下的一個或者多個XML配置文件)。
package com.junzerg.spring_knight;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class KnightMain {
@Autowired
Knight knight;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("META-INF/spring/knight.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}
1.4.2 Java配置的實現(xiàn)
對于Java配置的情況,使用AnnotationConfigApplicationContext。
package com.junzerg.spring_knight;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.junzerg.spring_knight.config.KnightConfig;
public class KnightMain {
@Autowired
Knight knight;
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext content = new AnnotationConfigApplicationContext(KnightConfig.class);
Knight knight = content.getBean(Knight.class);
knight.embarkOnQuest();
}
}
1.5 上下文中其他類和結(jié)果
1.5.1 上下文中其他類
- Knight
package com.junzerg.spring_knight;
public interface Knight {
void embarkOnQuest();
}
- Quest
package com.junzerg.spring_knight;
public interface Quest {
void embark();
}
1.5.2 運行結(jié)果:
Embarking on quest to slay the dragon!
1.5.3 Maven
我是在Maven中添加Spring依賴的,這一節(jié)只需要用到Spring的上下文(context)依賴,如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
2 面向切面編程(AOP)
通過面向切面編程(aspect-oriented programming, AOP),把遍布應用各處的功能分離出來形成可重用的組件。
AOP將橫切關(guān)注點(跨越系統(tǒng)的多個組件,例如日子,事務管理,安全等)模塊化,并以聲明的方式將它們應用到需要影響的組件中去,這樣能夠使這些組件有更高的內(nèi)聚性并且會更加關(guān)注自身的業(yè)務,不需要了解涉及系統(tǒng)服務帶來的復雜性,從而保證了POJO的簡單性。
2.1 AOP簡單應用
對于#1中的騎士的事跡,用一個吟游詩人來記載。
package com.junzerg.spring_knight;
import java.io.PrintStream;
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!");
}
}
Minstrel類,在騎士執(zhí)行每一個探險任務之前,singBeforeQuest被調(diào)用;在騎士完成探險任務之后,singAfterQuest()方法會被調(diào)用;這兩種情況都通過PrintStream類來歌頌事跡,這個類是通過構(gòu)造器來引入的。
**那么,在普通情況下,編寫這個類之后,會在BraceKnight中使用這個類。但是利用AOP,可以在騎士類不訪問吟游詩人類的情況下,讓吟游詩人歌頌騎士的事跡。
這種技術(shù)是通過在Spring配置文件中將Minstrel聲明為一個切面實現(xiàn)的。
minstrel.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" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<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">
<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" />
<aop:after pointcut-ref="embark" method="singAfterQuest" />
</aop:aspect>
</aop:config>
</beans>
2.3 結(jié)果和Maven
2.3.1 結(jié)果
Fa la la, the knight is so brave!
Embarking on quest to slay the dragon!
Tee hee hee, the brave knight did embark on a quest!
2.3.2 Maven
本節(jié)中利用到了AspectJ切點表達式,需要在Maven中添加如下依賴:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>