一、知識點
有時候,你可能有一組共用公用行為的類。在OOP中,它們必須擴充相同的基類或者實現相同的介面。這確實是可以用AOP模組化的一個橫切關注點。此外,Java的單繼承機制僅允許一個類最多繼承一個基類。所以,你不能同時從多個實作類別中繼承行為。
引入(Introduction)是AOP中一種特殊類型的通知。通過為一個介面提供實作類別,它允許對象動態地實現該介面。這看上去就像是對象在運行時繼承了實作類別。而且,你可以用多個實作類別將多個介面同時引入對象。這可以實現與多重繼承相同的效果。
二、程式碼範例
MaxCalculator和MinCalculator介面,用於定義max()和min()運算。
package com.codeproject.jackie.springrecipesnote.springaop;/** * @author jackie * */public interface MaxCalculator {public double max(double a, double b);}
package com.codeproject.jackie.springrecipesnote.springaop;/** * @author jackie * */public interface MinCalculator { public double min(double a, double b);}
相應的實作類別
package com.codeproject.jackie.springrecipesnote.springaop;/** * @author jackie * */public class MaxCalculatorImpl implements MaxCalculator {public double max(double a, double b) {double result = (a >= b) ? a : b;System.out.println("max(" + a + ", " + b + ") = " + result);return result;}}
package com.codeproject.jackie.springrecipesnote.springaop;/** * @author jackie * */public class MinCalculatorImpl implements MinCalculator {public double min(double a, double b) {double result = (a <= b) ? a : b;System.out.println("min(" + a + ", " + b + ") = " + result);return result;}}
如果希望ArithmeticCalculatorlmpl也執行max()和min()計算。由於Java語言僅支援單繼承,讓 ArithmeticCalculatorlmpl 類同時擴充 MaxCalculatorlmpl 和 MinCalculatorlmpl 是不可能的。唯一可能的方法是通過複製實現代碼或者將處理委派給實際的實作類別,擴充某個類 (例如MaxCalculatorlmpl)並實現另一個介面(例如MinCalculator)。在兩種情況下,你都必須重複方法的聲明。
有了引入,通過使用實作類別MaxCalculatorlmpl和MinCalculatorlmpl,可以使ArithmeticCalculatorlmpl動態地實現MaxCalculator和MinCalculator介面。這和從 MaxCalculatorlmpl 和 MinCalculatorlmpl多重繼承有相同的效果。引入的非凡之處在於不需要修改ArithmeticCalculatorlmpl類引入新的方法。
引入是通過動態代理在Spring AOP中實現這一切的。因為可以為動態代理指定一組需要實現的介面。引入就是通過向動態代理添加一個介面(例 如MaxCalculator)來工作的。當這個介面中聲明的方法在代理對象上調用時,代理將把調用委派給後端實作類別(例如MaxCalculatorlmpl)。
引入和通知類似,必須在一個aspect中聲明。可以建立一個新的aspect或者重用現有的aspect。在下面這個aspect中,可以使用@DeclareParents註解任意一個欄位來聲明引入。
package com.codeproject.jackie.springrecipesnote.springaop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.DeclareParents;/** * @author jackie * */@Aspectpublic class CalculatorIntroduction {@DeclareParents(value = "com.codeproject.jackie.springrecipesnote.springaop.ArithmeticCalculatorlmpl", defaultImpl = MaxCalculatorImpl.class)public MaxCalculator maxCalculator;@DeclareParents(value = "com.codeproject.jackie.springrecipesnote.springaop.ArithmeticCalculatorlmpl", defaultImpl = MinCalculatorImpl.class)public MinCalculator minCalculator;}
@DeclareParents註解的value屬性工作表示引入的目標類。引入的介面由被註解的欄位類型確定。最後,用於新介面的實作類別在defaultlmpl屬性中指定。
通過這兩個引入,你可以動態地為ArithmeticCalculatorlmpl類引入兩個介面。實際上,可以在@DeclareParents註解的value屬性中指定一個AspectJ類型匹配運算式,將一個介面引入多個類。最後需要在應用上下文中聲明這個aspect的一個執行個體。
<bean class="com.codeproject.jackie.springrecipesnote.springaop.CalculatorIntroduction" />
測試類別
package com.codeproject.jackie.springrecipesnote.springaop;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * @author jackie * */public class IntroductionTest {@Testpublic void testIntroduction() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator)applicationContext.getBean("arithmeticCalculator");arithmeticCalculator.add(1, 2);arithmeticCalculator.sub(1, 2);arithmeticCalculator.mul(1, 2);arithmeticCalculator.div(1, 2);MaxCalculator maxCalculator = (MaxCalculator) arithmeticCalculator;maxCalculator.max(1, 2);MinCalculator minCalculator = (MinCalculator) arithmeticCalculator;minCalculator.min(1, 2);}}