spring之AOP原始碼深入理解(一)aop攔截
(一) 原始碼角度
攔截機 (Interceptor), 是 AOP (Aspect-Oriented Programming) 的另一種叫法。AOP本身是一門語言,只不過我們使用的是基於JAVA的整合到Spring 中的 SpringAOP。同樣,我們將通過我們的例子來理解陌生的概念。
1、介面類
package com.test.TestSpring3; public interface UserService // 被攔截的介面 ...{ public void printUser(String user); }
2、實現類
package com.test.TestSpring3; public class UserServiceImp implements UserService // 實現UserService介面 ...{ public void printUser(String user) ...{ System.out.println("printUser user:" + user);// 顯示user } }
3、 AOP攔截器
package com.test.TestSpring3; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class UserInterceptor implements MethodInterceptor // AOP方法攔截器 ...{ public Object invoke(MethodInvocation arg0) throws Throwable ...{ try ...{ if (arg0.getMethod().getName().equals("printUser")) // 攔截方法是否是UserService介面的printUser方法 ...{ Object[] args = arg0.getArguments();// 被攔截的引數 System.out.println("user:" + args[0]); arg0.getArguments()[0] = "hello!";// 修改被攔截的引數 } System.out.println(arg0.getMethod().getName() + "---!"); return arg0.proceed();// 執行UserService介面的printUser方法 } catch (Exception e) ...{ throw e; } } }
(二)spring aop Aspect攔截器註解開發
Spring AOP 與ApectJ 的目的一致,都是為了統一處理橫切業務,但與AspectJ不同的是,Spring AOP 並不嘗試提供完整的AOP功能(即使它完全可以實現),Spring AOP 更注重的是與Spring IOC容器的結合,並結合該優勢來解決橫切業務的問題,因此在AOP的功能完善方面,相對來說AspectJ具有更大的優勢,同時,Spring注意到AspectJ在AOP的實現方式上依賴於特殊編譯器(ajc編譯器),因此Spring很機智迴避了這點,轉向採用動態代理技術的實現原理來構建Spring AOP的內部機制(動態織入),這是與AspectJ(靜態織入)最根本的區別。在AspectJ 1.5後,引入@Aspect形式的註解風格的開發,Spring也非常快地跟進了這種方式,因此Spring 2.0後便使用了與AspectJ一樣的註解。請注意,Spring 只是使用了與 AspectJ 5 一樣的註解,但仍然沒有使用 AspectJ 的編譯器,底層依是動態代理技術的實現,因此並不依賴於 AspectJ 的編譯器。下面我們先通過一個簡單的案例來演示Spring AOP的入門程式。
1、定義目標類介面和實現類:
//介面類
public interface UserDao {
int addUser();
void updateUser();
void deleteUser();
void findUser();
}
//實現類
import com.zejian.spring.springAop.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImp implements UserDao {
@Override
public int addUser() {
System.out.println("add user ......");
return 6666;
}
@Override
public void updateUser() {
System.out.println("update user ......");
}
@Override
public void deleteUser() {
System.out.println("delete user ......");
}
@Override
public void findUser() {
System.out.println("find user ......");
}
}
2、使用Spring 2.0引入的註解方式,編寫Spring AOP的aspect 類:
@Aspect
public class MyAspect {
/**
* 前置通知
*/
@Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public void before(){
System.out.println("前置通知....");
}
/**
* 後置通知
* returnVal,切點方法執行後的返回值
*/
@AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
public void AfterReturning(Object returnVal){
System.out.println("後置通知...."+returnVal);
}
/**
* 環繞通知
* @param joinPoint 可用於執行切點的類
* @return
* @throws Throwable
*/
@Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環繞通知前....");
Object obj= (Object) joinPoint.proceed();
System.out.println("環繞通知後....");
return obj;
}
/**
* 丟擲通知
* @param e
*/
@AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出現異常:msg="+e.getMessage());
}
/**
* 無論什麼情況下都會執行的方法
*/
@After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public void after(){
System.out.println("最終通知....");
}
}
3、編寫配置檔案交由Spring IOC容器管理:
<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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 啟動@aspectj的自動代理支援-->
<aop:aspectj-autoproxy />
<!-- 定義目標物件 -->
<bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
<!-- 定義aspect類 -->
<bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
</beans>
簡單說明一下,定義了一個目標類UserDaoImpl,利用Spring2.0引入的aspect註解開發功能定義aspect類即MyAspect,在該aspect類中,編寫了5種註解型別的通知函式,分別是前置通知@Before、後置通知@AfterReturning、環繞通知@Around、異常通知@AfterThrowing、最終通知@After,這5種通知與前面分析AspectJ的通知型別幾乎是一樣的,並註解通知上使用execution關鍵字定義的切點表示式,即指明該通知要應用的目標函式,當只有一個execution引數時,value屬性可以省略,當含兩個以上的引數,value必須註明,如存在返回值時。當然除了把切點表示式直接傳遞給通知註解型別外,還可以使用@pointcut來定義切點匹配表示式,這個與AspectJ使用關鍵字pointcut是一樣的,後面分析。目標類和aspect類定義完成後,最後需要在xml配置檔案中進行配置,同樣的所有類的建立都交由SpringIOC容器處理,注意,使用Spring AOP 的aspectJ功能時,需要使用以下程式碼啟動aspect的註解功能支援:
<aop:aspectj-autoproxy />