一、使用AspectJ 實現AOP
AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP語法所以它有一個專門的編譯器用來生成遵守Java字節編碼規範的Class文件。
Spring2.0之後 爲了簡化 AOP編程,支持AspectJ 技術
@AspectJ 是AspectJ1.5新增功能,通過JDK5註解技術,允許直接在Bean類中定義切面
新版本Spring框架,建議使用AspectJ方式來開發AOP ,而不需要使用傳統 Spring AOP 編程
1、基於註解的@AspectJ 編程
1) 下載 導入 aspectJ 開發包 (AspectJ 依賴 AOP 的 jar包 )
AspectJ開發包 com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
導入spring 整合 aspectj 的jar包 spring-aspects-3.2.0.RELEASE.jar
2) 編寫Spring配置文件 (需要aop名稱空間,使用Aspectj開發,我們需要AOP的schema)
使用 <aop:aspectj-autoproxy /> 配置自動代理
* 底層 <bean class="... AnnotationAwareAspectJAutoProxyCreator" />
3) @AspectJ 常用註解
@Aspect 定義切面通知類型
@Before 前置通知,相當於BeforeAdvice
@AfterReturning 後置通知,相當於AfterReturningAdvice
@Around 環繞通知,相當於MethodInterceptor
@AfterThrowing拋出通知,相當於ThrowAdvice
@After 最終final通知,不管是否異常,該通知都會執行
@DeclareParents 引介通知,相當於IntroductionInterceptor (不要求掌握)
4) 切點表達式定義
切點使用指定哪些連接點 會被增強 , 通過execution函數,可以定義切點的方法切入
語法: execution(<訪問修飾符>?<返回類型><方法名>(<參數>)<異常>)
例如:
execution(public * *(..)) 匹配所有public修飾符 任何方法
execution(* lsq.spring.service.HelloService.*(..)) 匹配HelloService類中所有方法,第一個*表示返回類型
execution(int lsq.spring.service.UserService.regist(..)) 匹配UserService中int返回類型 regist方法
execution(* lsq.spring.dao..*(..)) 匹配lsq.spring.dao包下 (包含子包) 所有類 所有方法
execution(* lsq.spring.dao.*(..)) 不包含子包
execution(* lsq.spring.dao.GenericDAO+.*(..)) 匹配GenericDAO 所有子類 或者 實現類 所有方法
開發步驟:
第一步 : 導入jar、 自動代理
第二步 : 編寫被代理對象 UserDAO
package lsq.spring.aspectj.a_annotation;
/**
* 被代理對象
*
* @author lishanquan
*
*/
public class UserDao {
public void save(){
System.out.println("用戶保存……");
}
public void delete(){
System.out.println("用戶刪除……");
}
public void update(){
System.out.println("用戶修改……");
}
public void search(){
System.out.println("用戶查詢……");
}
}
第三步 : 編寫切面類
package lsq.spring.aspectj.a_annotation;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 自定義切面類
*
* @author lishanquan
*
*/
@Aspect //聲明當前類是一個切面類
public class MyAspect {
//定義一個前置增強
@Before("execution(* lsq.spring.aspectj.a_annotation.UserDao.*(..))")//對UserDao中所有方法增強
public void before1(){
System.out.println("前置增強1……");
}
@Before("execution(* lsq.spring.aspectj.a_annotation.UserDao.save(..))")//對UserDao中save方法增強
public void before2(){
System.out.println("前置增強2……");
}
}
第四步: 配置applicationContext.xml 註冊被代理對象 和 切面類
<?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"
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">
<!-- 配置自動代理 -->
<aop:aspectj-autoproxy/>
<!-- 被代理目標 -->
<bean id="userDao" class="lsq.spring.aspectj.a_annotation.UserDao"></bean>
<!-- 切面配置 -->
<bean id="myAspect" class="lsq.spring.aspectj.a_annotation.MyAspect"></bean>
</beans>
測試:
package lsq.spring.aspectj.a_annotation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringTest {
//注入代理對象
@Autowired
@Qualifier("userDao")
private UserDao userDao;
@Test
public void demo(){
userDao.save();
userDao.delete();
userDao.update();
userDao.search();
}
}
顯示結果:
@AspectJ 支持通知類型詳解 :
第一種 前置通知 @Before : 前置通知不能攔截目標方法執行 , 每個前置通知方法接收JoinPoint
* JoinPoint 引包 org.aspectj.lang.JoinPoint
* JoinPoint 獲得攔截增強方法信息
第二種 後置通知 @AfterReturning : 後置通知,在目標方法執行,返回後 調用增強代碼
* 通過 returning 獲得目標方法返回值
<span style="white-space:pre"> </span>@AfterReturning(value = "execution(* lsq.spring.aspectj.a_annotation.UserDao.update(..))", returning = "returnValue")
// returnValue 是代理方法 ,參數名, 用來獲得目標業務方法執行後返回值
public void afterReturning(Object returnValue) {
System.out.println("後置增強.... 獲得方法返回值:" + returnValue);
}
* 參數爲ProceedingJoinPoint 可以調用攔截目標方法執行
<span style="white-space:pre"> </span>// 環繞增強
@Around("execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))")
// 可以阻止目標方法執行,通過參數
public Object around(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
System.out.println("環繞前增強...");
Object result = proceedingJoinPoint.proceed();// 執行目標方法
System.out.println("環繞後增強...");
return result;
}
如果 不寫 proceedingJoinPoint.proceed(); 目標方法就無法執行
第四種 拋出通知 @AfterThrowing : 在方法出現異常後,該方法獲得執行
* 在方法沒有錯誤時,不會得到執行
<span style="white-space:pre"> </span>// 拋出增強
@AfterThrowing(value = "execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))", throwing = "e")
// throwing 用來指定異常對象 參數名稱
public void afterThrowing(Throwable e) {
System.out.println("不好了,出問題了, " + e.getMessage());
}
第五種 最終通知 @After : 不管目標方法是否存在異常,都將執行 類似finally 代碼塊
<span style="white-space:pre"> </span>// 最終增強 ,無論目標方法是否有異常,都必須執行
@After("execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))")
public void after() {
System.out.println("這是必須執行的代碼 .....");
}
@Pointcut 切點定義
直接在通知上定義切點表達式,會造成一些切點表達式重複
在每個通知內定義切點,會造成工作量大,不易維護,對於重複的切點,可以使用@Poingcut進行定義
☆☆格式: private void 無參數方法,方法名爲切點名
<span style="white-space:pre"> </span>@Pointcut("execution(* lsq.spring.aspectj.a_annotation.UserDAO.search(..))")
private void mypointcut() {}
應用切點的通知
@After("MyAspect.mypointcut()")
public void after() {
System.out.println("這是必須執行的代碼 .....");
}
advisor是spring中aop定義的切面,通常由一個切點和一個通知組成。
aspect是規範中定義的切面,允許由多個切點和多個通知組成。
2、 基於XML配置 AspectJ 編程
1) 定義被代理對象 ProductDAO
package lsq.spring.aspectj.b_xml;
/**
* 被代理對象
*
* @author lishanquan
*
*/
public class ProductDao {
public void addProduct(){
System.out.println("添加商品……");
}
public int deleteProduct(){
System.out.println("刪除商品……");
return 1/0;
}
}
2) 定義切面 MyAspect
package lsq.spring.aspectj.b_xml;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 自定義切面類
* 切面類就是切點和通知的結合
* @author lishanquan
*
*/
public class MyAspect {
//前置增強
public void before(){
System.out.println("前置增強……");
}
//後置增強
public void afterReturing(Object returnVal){
System.out.println("後置增強……,返回值:"+returnVal);
}
//環繞增強
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("環繞前增強……");
Object result = proceedingJoinPoint.proceed();
System.out.println("環繞後增強……");
return result;
}
//異常增強
public void afterThrowing(Throwable ex){
System.out.println("發生異常,原因:"+ex.getMessage());
}
//最終通知
public void after(){
System.out.println("最終通知……");
}
}
3) 編寫Advice增強方法,在xml配置
<?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"
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">
<!-- 代理目標對象 -->
<bean id="productDao" class="lsq.spring.aspectj.b_xml.ProductDao"></bean>
<!-- 切面 -->
<bean id="myAspect" class="lsq.spring.aspectj.b_xml.MyAspect"></bean>
<!-- xml配置使用Aspectj -->
<!-- 進行aop配置 -->
<aop:config>
<!-- 定義切面 -->
<aop:aspect ref="myAspect">
<!-- 配置切點 -->
<aop:pointcut expression="execution(* lsq.spring.aspectj.b_xml.ProductDao.*(..))" id="myPointcut"/>
<!-- 通知 -->
<!-- <aop:before method="before" pointcut-ref="myPointcut"/> -->
<!-- <aop:after-returning method="afterReturing" returning="returnVal" pointcut-ref="myPointcut"/> -->
<!-- <aop:around method="around" pointcut-ref="myPointcut"/> -->
<!-- <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="myPointcut"/> -->
<aop:after method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
測試:
package lsq.spring.aspectj.b_xml;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext2.xml")
public class SpringTest {
@Autowired
@Qualifier("productDao")
private ProductDao productDao;
@Test
public void demo(){
productDao.addProduct();
productDao.deleteProduct();
}
}