Spring框架學習筆記(五)Spring Aop學習筆記(基於註解的Aop)

1.Aop:  面向切面編程 

具體的視圖可以這樣理解:

即通過切面來使代碼不冗餘,同樣也便於修改。

2. Aop術語

  1. 切面(Aspect):  橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象
  2. 通知(Advice):  切面必須要完成的工作 目標(Target): 被通知的對象
  3. 代理(Proxy): 向目標對象應用通知之後創建的對象
  4. 連接點(Joinpoint):程序執行的某個特定位置:如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連接點,執行點爲 ArithmethicCalculator#add(); 方位爲該方法執行前的位置
  5. 切點(pointcut):每個類都擁有多個連接點:例如 ArithmethicCalculator 的所有方法實際上都是連接點,即連接點是程序類中客觀存在的事務。AOP 通過切點定位到特定的連接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。切點和連接點不是一對一的關係,一個切點匹配多個連接點,切點通過 org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作爲連接點的查詢條件。

3. 用 AspectJ 註解聲明切面

具體步驟: 

  • 要在 Spring 中聲明 AspectJ 切面, 只需要在 IOC 容器中將切面聲明爲 Bean 實例.
  • 在 AspectJ 註解中, 切面只是一個帶有 @Aspect 註解的 Java 類. 
  • 通知是標註有某種註解的簡單的 Java 方法.

AspectJ 支持 5 種類型的通知註解:

@Before: 前置通知, 在方法執行之前執行
@After: 後置通知, 在方法執行之後執行 
@AfterRunning: 返回通知, 在方法返回結果之後執行
@AfterThrowing: 異常通知, 在方法拋出異常之後
@Around: 環繞通知, 圍繞着方法執行

首先寫一個接口類 一個實現類,用於實現代碼

接口類:

package SpringAop;

public interface Userformula {
	
	public int add(int i,int j);
	public int sub(int i,int j);
	public int mul(int i,int j);
	public int div(int i,int j);

}

實現類 (注意添加註解  @Component(value="userformula") )

package SpringAop;

import org.springframework.stereotype.Component;

@Component(value="userformula")
public class formulaimpl implements Userformula {
	@Override
	public int add(int i, int j) {
		// TODO 自動生成的方法存根
		int result=i+j;	
		return result;
	}
	@Override
	public int sub(int i, int j) {
		// TODO 自動生成的方法存根
        int result=i-j;	
		return result;	
		}
	@Override
	public int mul(int i, int j) {
		// TODO 自動生成的方法存根
        int result=i*j;	
		return result;
	}
	@Override
	public int div(int i, int j) {
		// TODO 自動生成的方法存根
        int result=i/j;	
		return result;
	}
}

創建切面類:

需要添加   @Component     @Aspect  兩個註解

① : 前置通知

創建方法, 添加前置通知

如果要獲取切入點的數據或者值,可以通過 JoinPoint 來獲取

@Before("execution(public int SpringAop.*.*(int, int))")
	public void beformethod(JoinPoint joinPoint) {
		//切入點方法名
		String methodName = joinPoint.getSignature().getName();
		//將切入點獲取的值傳入集合
		Object [] args = joinPoint.getArgs();
		
		System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
	}

②:後置通知 (同理與前置)

@After("execution(public int SpringAop.*.*(int, int))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " ends");
	}

③:返回通知 

//返回通知 可以通過returning="result" 以及方法名中加Object result 來獲取返回值
	@AfterReturning(value="execution(public int SpringAop.*.*(int, int))",
returning="result")
	public void afterreturnning(JoinPoint joinPoint,Object result) {
		String methodName=joinPoint.getSignature().getName();
		System.out.println("The method"+methodName+"result:"+result);
	}

 ④:異常通知

//異常通知  同返回通知一樣
	@AfterThrowing(value="execution(public int SpringAop.*.*(int, int))",throwing="ex")
	public void afterthrowig(JoinPoint joinPoint,Exception ex) {
		String methodname=joinPoint.getSignature().getName();
		System.out.println("The method"+methodname+"exception:"+ex);
	}

⑤:環繞通知(不常用)

將通知全部圍繞在一個裏面

@Around(value="execution(public int SpringAop.*.*(int, int))")
	public Object around(ProceedingJoinPoint pjd) {
		Object result = null;
		String methodName = pjd.getSignature().getName();
		
		try {
			//前置通知
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
			//執行目標方法
			result = pjd.proceed();
			//返回通知
			System.out.println("The method " + methodName + " ends with " + result);
		} catch (Throwable e) {
			//異常通知
			System.out.println("The method " + methodName + " occurs exception:" + e);
			throw new RuntimeException(e);
		}
		//後置通知
		System.out.println("The method " + methodName + " ends");
		
		return result;
	}

 

從上面可以看到每一個註解後面都需要添加exetution() 如果需要修改則需要修改多處,不符合現代代碼風格,違背設計模式。

我們需要通過一種方式來封裝起來。

利用   @Pointcut("execution(public int SpringAop.*.*(int, int))") 然後後面添加一個空方法,下面則只需要調用空方法就可以實現。

@Pointcut("execution(public int SpringAop.*.*(int, int))")
	public void point() {
		
	}
	

調用切入點方法實現前置通知。

@Before("point()")
	public void beformethod(JoinPoint joinPoint) {
		//切入點方法名
		String methodName = joinPoint.getSignature().getName();
		//將切入點獲取的值傳入集合
		Object [] args = joinPoint.getArgs();
		
		System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
	}

配置文件中添加Context 和 Aop

<context:component-scan base-package="SpringAop"></context:component-scan>
    <!-- 起到切面作用 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

寫一個主函數:

package SpringAop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class main {

	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		
		ApplicationContext ctx=new ClassPathXmlApplicationContext("Applicationcontext.xml");
		
		Userformula user=(Userformula) ctx.getBean("userformula");
		
		int r=user.add(3, 6);
		
		System.out.println("r的值爲: "+r);
		
		int s=user.div(100, 10);
		System.out.println("s的值爲"+s);	
	}

}

運行即可完成。

 

運行結果:

分別爲 前置通知    後置通知   返回通知  因爲無錯所以沒有報異常通知

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章