依賴注入是面型接口編程的一種體現,是Spring的核心思想。 事實上依賴注入並非什麼高深的技術, 只是被Sping這麼以包裝就顯得有些神祕。java
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語言打印helloworld字符串, 在這裏它不但依賴Language接口, 還依賴Java類,這使得它和Java類耦合在一塊兒。 要消除這種依賴或者說解耦很容易web
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類增長了設置具體語言的方法,使得Coder類只依賴Language接口而不依賴具體的語言實現,換言之,Coder類和具體的語言解耦了,此時咱們能夠垂手可得的使用其它語言代替Java,好比說使用C#spring
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(); }
這種在外部設置某個對象所依賴的具體對象的技巧就是依賴注入,這很很使人之外,一種最多見不過的編碼技巧竟然還有如此高大山的名稱。編程
對於Coder類來講,肯定使用何種語言本來實在編譯器期肯定的,使用依賴注入後,使用何種語言便延時至運行期。app
Spring框架的核心思想即是基於此,不過它的實現更進一步,它把建立各個對象設置依賴關係的過程動態化和通用化了。在咱們的代碼清單中,建立對象和設置依賴關係的main方法只適用與當前的狀況,而Spring的IOC容器能適用與任何狀況框架
一般,Spring的依賴關係由XML表示,IOC容器解析XML完成對象的建立和依賴注入。ide
咱們將以前的代碼用Spring框架來實現svg
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"); } }
依賴關係將由XML配置實現ui
<?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>
建立Coder對象的代碼變爲this
public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml"); Coder coder = (Coder) context.getBean("coder"); coder.helloWorld(); }
具體的對象建立和依賴關係的設置將由IOC根據XML配置來完成。
Spring使得依賴注入機制自動化,可是依賴注入的本質卻沒有變花。
面向切面編程能實現不改變原有代碼的前提下動態的對功能進行加強, 好比說在一個方法執行前或執行後作某些事情如記錄日誌、計算運行時間等等。
Spring中完美集成Aspectj,所以能夠很方便的進行面向切面編程。
Spring Aspectj有幾個註解用以實現經常使用的面向切面編程功能
@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) {} }
如上代碼所示, 此類對controller.Default類下的全部方法進行加強。
@Before註解修飾的方法會在被加強的方法執行前被執行
@After註解修飾的方法會在被加強的方法執行後被執行
@AfterReturning註解修飾的方法會在被加強的方法執行後被執行,但前提是被修飾的方法順利執行結束,假如方法中途拋出異常,那麼AfterReturning註解修飾的方法將不會被執行,而After註解修飾的方法是不管如何都會被執行。
@AfterThrowing註解修飾的方法會在被加強的方法執行出錯拋出異常的狀況下被執行。
@Around註解是@Before註解和@After註解的綜合,它能夠在被加強的方法的先後同時進行加強
@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()); } }
使用此註解,被加強的方法須要手動編碼調用
jp.proceed();
若是在加強代碼中不寫這一句,那麼被加強的方法將不會運行。
此外, 還有一個重要的註解 @Pointcut
此註解能夠用來提煉切入點
@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等註解能夠應用此註解聲明的切入點,從而減小代碼的重複。