1. 程式人生 > >談談對AOP的認識二——AOP概念術語理解

談談對AOP的認識二——AOP概念術語理解

一、AOP的術語

        上篇部落格介紹了AOP的由來,及使用aop的便利,在深入瞭解aop之前,我們當然得先了解一下其眾多的概念性術語:

        1、主術語

  • 切面(Aspect)

        一個關注點的模組化,這個關注點可能會橫切多個物件。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面可以使用基於模式或者基於@Aspect註解的方式來實現。

  • 連線點(Joinpoint)
        在程式執行過程中某個特定的點,比如某方法呼叫的時候或者處理異常的時候。在Spring AOP中,一個連線點總是表示一個方法的執行。

  • 通知(Advice)
        在切面的某個特定的連線點上執行的動作。其中包括了“around”、“before”和“after”等不同型別的通知(通知的型別將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,並維護一個以連線點為中心的攔截器鏈。

  • 切入點(Pointcut)
        匹配連線點的斷言。通知和一個切入點表示式關聯,並在滿足這個切入點的連線點上執行(例如,當執行某個特定名稱的方法時)。切入點表示式如何和連線點匹配是AOP的核心:Spring預設使用AspectJ切入點語法。

  • 引入(Introduction)
        用來給一個型別宣告額外的方法或屬性(也被稱為連線型別宣告(inter-type declaration))。Spring允許引入新的介面(以及一個對應的實現)到任何被代理的物件。例如,你可以使用引入來使一個bean實現IsModified介面,以便簡化快取機制。

  • 目標物件(Target Object)
        被一個或者多個切面所通知的物件。也被稱做被通知(advised)物件。 既然Spring AOP是通過執行時代理實現的,這個物件永遠是一個被代理(proxied)物件。

  • AOP代理(AOP Proxy)
        AOP框架建立的物件,用來實現切面契約(例如通知方法執行等等)。在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。

  • 織入(Weaving)
        把切面連線到其它的應用程式型別或者物件上,並建立一個被通知的物件。這些可以在編譯時(例如使用AspectJ編譯器),類載入時和執行時完成。Spring和其他純Java AOP框架一樣,在執行時完成織入。

   2、通知型別:

  • 前置通知(Before advice)
        在某連線點之前執行的通知,但這個通知不能阻止連線點之前的執行流程(除非它丟擲一個異常)。

  • 後置通知(After returning advice)
        在某連線點正常完成後執行的通知:例如,一個方法沒有丟擲任何異常,正常返回。

  • 異常通知(After throwing advice)
        在方法丟擲異常退出時執行的通知。

  • 最終通知(After (finally) advice)
        當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。

  • 環繞通知(Around Advice)
        包圍一個連線點的通知,如方法呼叫。這是最強大的一種通知型別。環繞通知可以在方法呼叫前後完成自定義的行為。它也會選擇是否繼續執行連線點或直接返回它自己的返回值或丟擲異常來結束執行。

    環繞通知是最常用的通知型別。和AspectJ一樣,Spring提供所有型別的通知,推薦使用盡可能簡單的通知型別來實現需要的功能。例如,如果你只是需要一個方法的返回值來更新快取,最好使用後置通知而不是環繞通知,儘管環繞通知也能完成同樣的事情。用最合適的通知型別可以使得程式設計模型變得簡單,並且能夠避免很多潛在的錯誤。比如,你不需要在JoinPoint上呼叫用於環繞通知的proceed()方法,就不會有呼叫的問題。

二、程式碼演示  

   1、業務物件——要切入的目標

     業務類跟有沒有AOP沒有多大關係,業務類是不知道AOP的存在的。

<span style="font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.jushi.aop;

import java.util.HashMap;
import java.util.Map;

public class IUserImpl implements IUser {

	public static Map map = null;
	public static void init(){
		String[] list = {"Lucy", "Tom", "小明", "Smith", "Hello"};
		Map tmp = new HashMap();
		for(int i=0; i<list.length; i++){
			tmp.put(list[i], list[i]+"00");
		}
		map = tmp;
	}
	public void addUser(String username) {
		init();
		map.put(username, username+"11");
		System.out.println("--------------【addUser】: "+username+" --------------");
		System.out.println("【The new List:"+map+"】");
	}

	public void findAll() {
		init();
		System.out.println("---------------【findAll】: "+map+" ------------------");
	}

	public String findUser(String username) {
		init();
		String password = "沒查到該使用者";
		if(map.containsKey(username)){
			password = map.get(username).toString();
		}
		System.out.println("-----------------【findUser】-----------------");
		System.out.println("-----------------Username:"+username+"-----------------");
		System.out.println("-----------------【Result】:"+password+"------------------");
		return password;
		
	}

}
</span></span>

    2、定義切面類:定義了切入服務,通知型別,切入點。

<span style="font-size:18px;">public class CheckUser {

	//定義切入點
	//被攔截指定包中以find開頭任何返回值,任意引數的方法。
	@Pointcut("execution(* com.jushi.aop.*.find*(..))")
	public void checkUser(){
		System.out.println("**************The System is Searching Information For You****************");
	}
	//定義切入點
	//被攔截指定包中以add開頭任何返回值,任意引數的方法。
	@Pointcut("execution(* com.jushi.aop.*.add*(..))")
	public void checkAdd(){
		System.out.println("**************<< Add User >> Checking.....***************");
	}
	//前置通知,在執行目標物件方法之前執行
	@Before("checkUser()")
	public void beforeCheck(){
		System.out.println(">>>>>>>> 準備搜查使用者..........");
	}
	//後置通知,在執行目標物件方法之後執行
	@After("checkUser()")
	public void afterCheck(){
		System.out.println(">>>>>>>> 搜查使用者完畢..........");
	}

	//前置通知,在執行目標物件方法之前執行
	@Before("checkAdd()")
	public void beforeAdd(){
		System.out.println(">>>>>>>> 增加使用者--檢查ing..........");
	}
	
	//後置通知,在執行目標物件方法之後執行
	@After("checkAdd()")
	public void afterAdd(){
		System.out.println(">>>>>>>> 增加使用者--檢查完畢!未發現異常!..........");
	}
}</span>

    3、現在我們需要配置Spring的配置檔案,告訴程式掃描AOP的註解。

<span style="font-size:18px;">@Aspect
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans 
	   					   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	   					   http://www.springframework.org/schema/aop 
						   http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
						   http://www.springframework.org/schema/tx 
						   http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	
	//宣告自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面
	<aop:aspectj-autoproxy/>
	// 業務類
	<bean id="user" class="com.jushi.aop.IUserImpl"/>
	//切面類
	<bean id="check" class="com.jushi.aop.CheckUser"/>
</beans></span>

    至此,spring aop 的一個簡單Demo 就實現了。