Spring基礎知識(7)-Aspectj

一、使用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);
	}


      第三種 環繞通知 @Around : 在目標方法前後增強 ,阻止目標方法執行 
      * 參數爲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 和 aspect 區別 ?

      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();
	}
}







      

      

  






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