依賴注入
依賴注入是面型接口編程的一種體現(xiàn)肥隆,是Spring的核心思想愕贡。 事實(shí)上依賴注入并不是什么高深的技術(shù), 只是被Sping這么以包裝就顯得有些神秘巷屿。
class Main {
interface Language {
void print(String s);
}
static class Java implements Language{
@Override
public void print(String x) {
System.out.println("System.out.print(\""+ x +"\")");
}
}
static class Coder {
private Language lang = new Java();
public void helloWorld() {
lang.print("hello world");
}
}
public static void main(String[] args) {
Coder coder = new Coder();
coder.helloWorld();
}
}
如上代碼清單所示固以,Coder使用Java語(yǔ)言打印helloworld字符串, 在這里它不但依賴Language接口嘱巾, 還依賴Java類(lèi)憨琳,這使得它和Java類(lèi)耦合在一起。 要消除這種依賴或者說(shuō)解耦很容易
interface Language {
void print(String s);
}
static class Java implements Language{
@Override
public void print(String x) {
System.out.println("System.out.print(\""+ x +"\")");
}
}
static class Coder {
private Language lang;
public void setLang(Language lang) {
this.lang = lang;
}
public void helloWorld() {
lang.print("hello world");
}
}
public static void main(String[] args) {
Coder coder = new Coder();
Language java = new Java();
coder.setLang(java);
coder.helloWorld();
}
}
我們給Coder類(lèi)增加了設(shè)置具體語(yǔ)言的方法旬昭,使得Coder類(lèi)只依賴Language接口而不依賴具體的語(yǔ)言實(shí)現(xiàn)篙螟,換言之,Coder類(lèi)和具體的語(yǔ)言解耦了问拘,此時(shí)我們可以輕而易舉的使用其它語(yǔ)言代替Java遍略,比如說(shuō)使用C#
static class CSharp implements Language{
@Override
public void print(String x) {
System.out.println("Console.Write(\""+ x +"\")");
}
}
public static void main(String[] args) {
Coder coder = new Coder();
Language csharp = new CSharp();
coder.setLang(csharp);
coder.helloWorld();
}
這種在外部設(shè)置某個(gè)對(duì)象所依賴的具體對(duì)象的技巧就是依賴注入,這很很令人以外骤坐,一種最常見(jiàn)不過(guò)的編碼技巧居然還有如此高大山的名稱(chēng)绪杏。
對(duì)于Coder類(lèi)來(lái)說(shuō),確定使用何種語(yǔ)言原本實(shí)在編譯器期確定的纽绍,使用依賴注入后蕾久,使用何種語(yǔ)言便延時(shí)至運(yùn)行期。
Spring框架的核心思想便是基于此拌夏,不過(guò)它的實(shí)現(xiàn)更進(jìn)一步僧著,它把創(chuàng)建各個(gè)對(duì)象設(shè)置依賴關(guān)系的過(guò)程動(dòng)態(tài)化和通用化了。在我們的代碼清單中障簿,創(chuàng)建對(duì)象和設(shè)置依賴關(guān)系的main方法只適用與當(dāng)前的情況盹愚,而Spring的IOC容器能適用與任何情況
通常,Spring的依賴關(guān)系由XML表示站故,IOC容器解析XML完成對(duì)象的創(chuàng)建和依賴注入皆怕。
我們將之前的代碼用Spring框架來(lái)實(shí)現(xiàn)
interface Language {
void print(String s);
}
class Java implements Language{
@Override
public void print(String x) {
System.out.println("System.out.print(\""+ x +"\")");
}
}
class CSharp implements Language{
@Override
public void print(String x) {
System.out.println("Console.Write(\""+ x +"\")");
}
}
class Coder {
private Language lang;
public void setLang(Language lang) {
this.lang = lang;
}
public Language getLang() {
return lang;
}
public void helloWorld() {
lang.print("hello world");
}
}
依賴關(guān)系將由XML配置實(shí)現(xiàn)
<?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="java" class="Java">
</bean>
<bean id="csharp" class="CSharp">
</bean>
<bean id="coder" class="Coder">
<property name="lang" ref="csharp"></property>
</bean>
</beans>
創(chuàng)建Coder對(duì)象的代碼變?yōu)?/p>
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");
Coder coder = (Coder) context.getBean("coder");
coder.helloWorld();
}
具體的對(duì)象創(chuàng)建和依賴關(guān)系的設(shè)置將由IOC根據(jù)XML配置來(lái)完成。
Spring使得依賴注入機(jī)制自動(dòng)化世蔗,但是依賴注入的本質(zhì)卻沒(méi)有變花端逼。
面向切面編程
面向切面編程能實(shí)現(xiàn)不改變?cè)写a的前提下動(dòng)態(tài)的對(duì)功能進(jìn)行增強(qiáng)朗兵, 比如說(shuō)在一個(gè)方法執(zhí)行前或執(zhí)行后做某些事情如記錄日志污淋、計(jì)算運(yùn)行時(shí)間等等。
Spring中完美集成Aspectj余掖,因此可以很方便的進(jìn)行面向切面編程寸爆。
Spring Aspectj有幾個(gè)注解用以實(shí)現(xiàn)常用的面向切面編程功能
@Aspect
public class Logger {
@Before("execution(* controller.Default.*(..))")
public void before(JoinPoint join){}
@After("execution(* controller.Default.*(..))")
public void after(){}
@AfterReturning("execution(* controller.Default.*(..))")
public void afterReturning() {}
@AfterThrowing("execution(* controller.Default.*(..))")
public void afterThrowing(){}
@Around("execution(* controller.Default.*(..))")
public void around(ProceedingJoinPoint jp) {}
}
如上代碼所示礁鲁, 此類(lèi)對(duì)controller.Default類(lèi)下的所有方法進(jìn)行增強(qiáng)。
@Before注解
@Before注解修飾的方法會(huì)在被增強(qiáng)的方法執(zhí)行前被執(zhí)行
@After注解
@After注解修飾的方法會(huì)在被增強(qiáng)的方法執(zhí)行后被執(zhí)行
@AfterReturning注解
@AfterReturning注解修飾的方法會(huì)在被增強(qiáng)的方法執(zhí)行后被執(zhí)行赁豆,但前提是被修飾的方法順利執(zhí)行結(jié)束仅醇,假如方法中途拋出異常,那么AfterReturning注解修飾的方法將不會(huì)被執(zhí)行魔种,而After注解修飾的方法是無(wú)論如何都會(huì)被執(zhí)行析二。
@AfterThrowing注解
@AfterThrowing注解修飾的方法會(huì)在被增強(qiáng)的方法執(zhí)行出錯(cuò)拋出異常的情況下被執(zhí)行。
@Around注解
@Around注解是@Before注解和@After注解的綜合节预,它可以在被增強(qiáng)的方法的前后同時(shí)進(jìn)行增強(qiáng)
@Around("execution(* controller.Default.*(..))")
public void around(ProceedingJoinPoint jp) {
try {
System.out.println("before");
jp.proceed();
System.out.println("after");
} catch (Throwable e) {
System.out.println(e.getMessage());
}
}
使用此注解叶摄,被增強(qiáng)的方法需要手動(dòng)編碼調(diào)用
jp.proceed();
如果在增強(qiáng)代碼中不寫(xiě)這一句,那么被增強(qiáng)的方法將不會(huì)運(yùn)行安拟。
此外蛤吓, 還有一個(gè)重要的注解 @Pointcut
@Pointcut注解
此注解可以用來(lái)提煉切入點(diǎn)
@Aspect
public class Logger {
@Pointcut( value = "execution(* controller.Default.*(..))")
public void pointcut() {}
@Before("pointcut()")
public void before(JoinPoint join){}
@After("pointcut()")
public void after(){}
@AfterReturning("pointcut()")
public void afterReturning() {}
@AfterThrowing("pointcut()")
public void afterThrowing(){}
@Around("pointcut()")
public void around(ProceedingJoinPoint jp) {}
}
@Before、@After等注解可以應(yīng)用此注解聲明的切入點(diǎn)糠赦,從而減少代碼的重復(fù)会傲。