1 依賴注入
通過依賴注入(Dependency Injection, DI)扰她,對(duì)象的依賴關(guān)系將由系統(tǒng)中負(fù)責(zé)協(xié)調(diào)各對(duì)象的第三方組件在創(chuàng)建對(duì)象的時(shí)候進(jìn)行設(shè)定纺涤。
1.1 實(shí)現(xiàn)
對(duì)于如下類
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)建探險(xiǎn)任務(wù)湖笨,而是在構(gòu)造的時(shí)候把探險(xiǎn)任務(wù)作為構(gòu)造器參數(shù)傳入颁湖。這是依賴注入的一種方式嘀趟,構(gòu)造器注入(constructor injection)雄驹。
- 傳入的探險(xiǎn)類型Quest,是所有的探險(xiǎn)任務(wù)都必須實(shí)現(xiàn)的一個(gè)接口荣堰。
- BraveKnight沒有與特定的Quest實(shí)現(xiàn)發(fā)生耦合床未,只要求探險(xiǎn)任務(wù)實(shí)現(xiàn)了Quest接口。
可以看出:
DI帶來的最大的好處就是松耦合振坚。如果一個(gè)對(duì)象只通過接口(而不是具體實(shí)現(xiàn)或者初始化過程)來表明依賴關(guān)系薇搁,那么這種依賴就能在對(duì)象本身毫不知情的情況下,用不同的具體實(shí)現(xiàn)進(jìn)行替換渡八。
1.2 注入
對(duì)于#1中的BraveKnight類啃洋。可以接受任意一種Quest的實(shí)現(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)建了這個(gè)類之后宏娄,剩下的就是將這個(gè)類交給BraveKnight。這就是創(chuàng)建應(yī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的時(shí)候傳入SlayDragonQuest bean的應(yīng)用十饥,作為構(gòu)造器的參數(shù)。
- 在構(gòu)造SlayDragonQuest bean的時(shí)候?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 實(shí)現(xiàn)
1.4.1 xml配置的實(shí)現(xiàn)
對(duì)于xml配置的情況,使用ClassPathXmlApplicationContext(加載位于應(yīng)用程序類路徑下的一個(gè)或者多個(gè)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配置的實(shí)現(xiàn)
對(duì)于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 運(yùn)行結(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),把遍布應(yīng)用各處的功能分離出來形成可重用的組件帅刊。
AOP將橫切關(guān)注點(diǎn)(跨越系統(tǒng)的多個(gè)組件纸泡,例如日子,事務(wù)管理赖瞒,安全等)模塊化女揭,并以聲明的方式將它們應(yīng)用到需要影響的組件中去蚤假,這樣能夠使這些組件有更高的內(nèi)聚性并且會(huì)更加關(guān)注自身的業(yè)務(wù),不需要了解涉及系統(tǒng)服務(wù)帶來的復(fù)雜性吧兔,從而保證了POJO的簡單性磷仰。
2.1 AOP簡單應(yīng)用
對(duì)于#1中的騎士的事跡,用一個(gè)吟游詩人來記載境蔼。
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í)行每一個(gè)探險(xiǎn)任務(wù)之前,singBeforeQuest被調(diào)用箍土;在騎士完成探險(xiǎn)任務(wù)之后逢享,singAfterQuest()方法會(huì)被調(diào)用;這兩種情況都通過PrintStream類來歌頌事跡吴藻,這個(gè)類是通過構(gòu)造器來引入的拼苍。
**那么,在普通情況下调缨,編寫這個(gè)類之后,會(huì)在BraceKnight中使用這個(gè)類吆你。但是利用AOP弦叶,可以在騎士類不訪問吟游詩人類的情況下,讓吟游詩人歌頌騎士的事跡妇多。
這種技術(shù)是通過在Spring配置文件中將Minstrel聲明為一個(gè)切面實(shí)現(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切點(diǎn)表達(dá)式,需要在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>