在學習AOP以前,咱們先簡單理解下IOC和AOP的原理。java
IOC:控制反轉編程
簡單明白什麼是控制反轉app
傳統的面向對象編程是在一個類裏須要哪一個對象就new一個,new多了,就會形成各個類彼此之間的依賴性很高,甚至會形成一個對象出了個問題,致使其餘類對象都不能運行的狀況。因此這就是所謂的耦合度很高。ide
那麼確定須要一個方法來解耦啦,那就是ioc,ioc它是一個容器,在容器裏存放的是bean對象,那麼一個類要成爲ioc內置的bean對象,須要在java類裏面加入相關的註解聲明,有了這個聲明,ioc容器加載時就會全權管理這個類對象的一切,包括生命週期,也能夠理解ioc是beanFactory的升級版,咱們知道,beanFactory一般是在須要的類裏直接靜態或者繼承關係來得到beanFactory裏面的對象或者方法的,這種方法的依賴性仍是有的,那麼做爲它的升級版ioc,那就不同了,它能夠說徹底不依賴任何java文件,而是哪一個java類須要某個對象,直接注入提供給他,極大的下降了耦合度。學習
DI:依賴注入。其實和IOC是一個原理,它只是基於IOC思想的一個實現測試
依賴注入有三種方法spa
依賴注入很簡單,不作描述,通常都是配置自動掃描,而後自動注入@autowrite代理
AOP:面向切面日誌
說實話也是ioc思想的衍生,面向對象完善,是一個很值得研究深透的一個思想。具體東西不說,直接簡單點解釋。在開發中,好比要作一個日誌監控功能,功能要求每一個方法先後都要後臺打印執行的開始,結束時間,咱們能夠寫一個接口或者繼承來實現,但總管這樣,效率依舊很低。咱們能夠這樣子想:既然這些方法都要一個日誌功能,傳統的面向接口不太好,那麼咱們就用aop面向切面吧! 把日誌功能當成一個切面,而這個切面是由許多切點構成的,想一下:不少個點,而後就成了一個面。。可是這些點的業務需求是同樣的,只是實現不同,不然有其餘的點混入進來,這個「面」就不完美了,不完美那就不提倡了。那麼什麼是點呢?點就是鏈接點,一個方法裏先後加入日誌功能,那麼它就有兩個點,一前一後,這兩個點咱們就能夠在抽出來的那個所謂的 "面 "的類中插入日誌功能,咱們能夠經過註解或者xml來插入,有5種插入順序,下面的列子會看到,其內部經過java的動態代理來實現,好,上個經典的例子。。component
配置文件
<!-- 自動掃描 -->
<context:component-scan base-package="aop"></context:component-scan>
<!-- 自動代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
有兩個類:一個接口:ArithmeticCalculator 一個實現:ArithmeticCalculatorImpl
接口定義了簡單的加減乘除
public interface ArithmeticCalculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
接口的實現,返回結果
@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
接下來 我要在每一個方法裏先後,或者出現異常的時候都打印日誌,由於這些功能都是相同的,並且每一個方法裏都有接入點,因此就組成一個切面類:AspectjTest.class
@Component // 加入到ioc容器中,讓其自動掃描
@Aspect // 聲明爲一個切面
public class AspectjTest {
// 前置通知,在目標方法開始前執行
@Before(value = "execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
public void beforeMthod(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
List<Object> largs = (List) Arrays.asList(args);
System.out.println("the methood " + methodName + " begins with " + largs);
}
//後置通知,在目標方法執行完成後,不管結果有沒有異常,都會執行的一個通知
//在後置通知裏還不能訪問目標方法的結果
@After(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
public void afterMethod(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
System.out.println("the methood " + methodName + " ends");
}
//運行結束通知,在目標方法正常執行完成後的一個通知
//能夠訪問到返回結果
@AfterReturning(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))",returning="result")
public void afterReturnRunMethod(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("the methood " + methodName + " ends with: "+result);
}
//異常通知,在目標方法執行時發生異常返回的一個通知
//能夠訪問到異常結果
@AfterThrowing(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))",throwing="expection")
public void afterThrowingMethod(JoinPoint joinPoint,Object expection){
String methodName = joinPoint.getSignature().getName();
System.out.println("the methood "+methodName+" throw: "+expection );
}
//環繞通知:需攜帶ProceedingJoinPoint類型的參數
//環繞通知相似動態代理的全過程,ProceedingJoinPoint 類型的參數能夠決定是否執行目標方法
//環繞通知必須有返回值,返回值爲目標方法的返回值
@Around(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint pjp){
String methodName = pjp.getSignature().getName();
Object result = null;
try {
//前置通知
System.out.println("the methood " + methodName + " begins with " +Arrays.asList(pjp.getArgs()));
//執行目標方法
result = pjp.proceed();
//運行結束通知(正常結束)
System.out.println("the methood " + methodName + " ends with: "+result);
} catch (Throwable e) {
//異常通知
System.out.println("the methood "+methodName+" throw: "+e);
}
//後置通知(無論有沒有異常都會返回)
System.out.println("the methood " + methodName + " ends");
return result;
}
}
客戶端測試下:
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
ArithmeticCalculator ar = (ArithmeticCalculator) ctx.getBean("arithmeticCalculatorImpl");
int result = ar.add(1, 2);
System.out.println("result:"+result);
int result2 = ar.div(3, 1);
System.out.println("result2:"+result2);
}
}
觀察控制檯,結合上述,便能理解AOP的美妙之處