spring心得10--使用Aspectj進行AOP開發介紹及案例分析

y_keven發表於2013-05-09

1.使用Aspectj進行AOP開發

使用aspectjs的操作步驟:

1)新增類庫:aspectjrt.jar和aspectjweaver.jar

2)新增aop schema.

3)定義xml元素:<aop:aspectj-autoproxy>

4)編寫java類,並用@Aspect註解成通知

AspectJ支援 5 種型別的通知註解:

  @Before: 前置通知, 在方法執行之前執行

  @After: 後置通知, 在方法執行之後執行

  @AfterReturning: 返回通知, 在方法返回結果之後執行

  @AfterThrowing: 異常通知, 在方法丟擲異常之後

  @Around: 環繞通知, 圍繞著方法執行

   配置成普通bean元素即可;下面簡單介紹這幾種通知的術語和使用方式,後面會通過案例分析的方式來剖解aspectjs通知程式設計。

前置通知:@Before

@Aspect

public class AudienceAdvice {

   @Before("execution(* WelcomeService.*(..))")

   public void takeSeats(){..}

   @Before("execution(* WelcomeService.*(..))")

   public void turnOffCellphone(JoinPoint jp){..}

   JoinPoint引數可訪問連線點細節,切入方法名和引數等.

jp.getTarget()//目標物件

jp.getThis()//當前的代理物件

jp.getArgs();//方法呼叫引數

jp.getSignature().getName()//方法簽名

後置通知:@After

    @After("execution(* *..WelcomeService.*(..))")

    public void applaud(){..}

    後置通知在目標方法執行完成之後執行.一個切面aspect包含很多通知.後置通知表明目標方法執行完之後,不論是否拋異常,都會織入該通知.

返回通知:@AfterReturning 

    方法返回後通知只在目標方法返回以後執行,若拋異常不執行.

@AfterReturning(pointcut="",returning="res")

public void xxx(Joinput jp,Object res)

   在AfterReturning通知中可接收到返回值.res即是用來接收返回值的物件.

環繞通知:@Around

    @Around("execution(* *..WelcomeService.*(..))")

    public void around(ProceedingPointCut jp){..}

   注意:可以控制目標方法是否呼叫,以及返回完全不同的物件,要慎用.

指定優先順序:

@Aspect

@Order(0)

public class xxx{...}

    加上@Order註解可以指定加入切面的優先順序(先後順序,值越小,優先順序越高)

典型Aspectj切入點表示式定義:

execution(* cn.itcast.WelcomeServiceImpl.*(..))

execution(public * *..WelcomeServiceImpl.*(..))

execution(public void *..WelcomeServiceImpl.*(*))

execution(public void *..*Service.*(double,double))..

切入點表示式運算(&& || !)

@Pointcut("execution(..) || execution(..)")

重用切入點定義

將切入點註解到一個空的方法體上,其它地方引用即可.

   //定義切入點

   @Pointcut("execution(* *..WelcomeService.*(..))")

   public void performPoint(){}

   @Before("performPoint()")

   @After("performPoint()")

引入通知:

@Aspect

public class MyAspectjIntroduction {

   @DeclareParents(value="*..*Service*", defaultImpl=ModifyDateImpl.class)

   private ModifyDate md ;

}

    value:指定哪些類可以應用該屬性

    defaultImpl:指定介面的實現類

使用pojo+xml開發aop

    基於註解的aspectj宣告優先於xml配置.

    基於xml的配置是spring專有的.aspectj得到越來越多的支援,

具備更好的重用性.

 

2.Aspectj案例分析

    下面通過兩個案例分別來闡釋兩種不同方法使用aspectj開發的專案;

    首先列出這兩種方式都用到的目標抽象介面和抽象介面的實現類目標類UserService和UserServiceImpl;以及引用通知需要的介面和實現類

UserService.java

package www.csdn.spring.proxy.advice.aspectjs;

public interface UserService {

	public void save(Object entity);
	public void update(Object entity);
	public void delete(Object entity);
	public void getAllObjects();
}
UserServiceImpl.java
package www.csdn.spring.proxy.advice.aspectjs;

public class UserServiceImpl implements UserService {

	@Override
	public void save(Object entity) {
		System.out.println("---儲存方法:save()---");
	}

	@Override
	public void update(Object entity) {
		System.out.println("---更新方法:update()---");
	}

	@Override
	public void delete(Object entity) {
		System.out.println("---刪除方法:delete()---");
		//int i = 1/0;
	}

	@Override
	public void getAllObjects() {
		System.out.println("---查詢所有方法:getAllObjects()---");
	}

}
   下面是引用通知需要的介面和實現類:
Auditable.java
package www.csdn.spring.proxy.advice.aspectjs;

import java.util.Date;

public interface Auditable {
	
	public void setDate(Date date);
	public Date getDate();

}
AuditableImpl.java
package www.csdn.spring.proxy.advice.aspectjs;

import java.util.Date;

import org.springframework.aop.support.DelegatingIntroductionInterceptor;

public class AuditableImpl extends DelegatingIntroductionInterceptor implements Auditable {

	private Date date;
	@Override
	public void setDate(Date date) {
		this.date = date;
	}

	@Override
	public Date getDate() {
		return date;
	}
}


 

第一種方式:用的advice通知類:AdviceService.java

package www.csdn.spring.proxy.advice.aspectjs;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.junit.experimental.theories.Theory;

@Aspect
public class AdviceService {
	
	//前置通知
	@Before(value = "execution(* UserServiceImpl.save(..))")
	public void beforeMethod(){
		System.out.println("-------開啟事務-------");
	}
	
	//後置通知
	@After(value =  "execution(* www.csdn..UserServiceImpl.save(*))")
	public void afterMethod(){
		System.out.println("-------結束事務--------");
	}
	
	//環繞通知
	@Around(value = "execution(* UserServiceImpl.update(*))")
	public void aroundMethod(ProceedingJoinPoint pjp){
		System.out.println("-=-=-=-=安全處理之前-=-=-=-");
		try {
			pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("-=-=-=-=-=安全處理之後-=-=-=-=-");
	}
	
	//定義一個指定切入點位置的方法
	@Pointcut("execution(* *..UserServiceImpl.delete(*))")
	public void pointCuts(){}
	
	//下面兩個通知引用該切入點方法
	//異常通知
	/*@AfterThrowing("pointCuts()")
	public void throwsMethod(){
		System.out.println("異常通知執行");
	}*/
	
	@AfterReturning("pointCuts()")
	public void afterRuning(){
		System.out.println("=========返回值========");
	}
}


 

使用aspectj最重要的一個檔案,spring相關的配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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,實際是aspectjs自動掃描所有bean,去找有切面的bean -->
	<bean id="adviceService" class="www.csdn.spring.proxy.advice.aspectjs.AdviceService" />
    <!-- 引入通知切面 -->
	<bean id="auditableService" class="www.csdn.spring.proxy.advice.aspectjs.AuditableService" />


	<!-- 真實主題 目標物件 -->
	<bean id="userServiceImpl" class="www.csdn.spring.proxy.advice.aspectjs.UserServiceImpl" />

	<!-- 使用aspectjs配置自動代理 -->
	<aop:aspectj-autoproxy />

</beans>


 

測試類 AdviseTest.java

package www.csdn.spring.proxy.advice.aspectjs;

import java.util.Date;

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

public class AdviseTest {

	@Test
	public void testUser() {

		ApplicationContext context = new ClassPathXmlApplicationContext(
				"spring-aspectjs.xml");
		// 代理主題角色,這裡用代理主題和真實主題實現的同樣介面類來接收建立的代理主題,就是建立一個目標類
		UserService userService = context.getBean("userServiceImpl",
				UserService.class);
		
		userService.save(null);
		/*userService.update(null);
		try{			
			userService.delete(null);
		}catch(Exception e){
			
		}		
		userService.getAllObjects();*/
		
		//引入通知測試
		Auditable auditable = (Auditable) userService;
		auditable.setDate(new Date());
		System.out.println(auditable.getDate());
	}

}


 

第二種方式 :使用pojo-xml編寫aspectj的通知類AdviceService.java

package www.csdn.spring.proxy.advice.aspectjs.pojoxml;

import org.aspectj.lang.ProceedingJoinPoint;

public class AdviceService {
	
	//前置通知
	public void beforeMethod(){
		System.out.println("-------開啟事務-------");
	}
	
	//後置通知
	public void afterMethod(){
		System.out.println("-------結束事務--------");
	}
	
	//環繞通知
	public void aroundMethod(ProceedingJoinPoint pjp){
		System.out.println("-=-=-=-=安全處理之前-=-=-=-");
		try {
			pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("-=-=-=-=-=安全處理之後-=-=-=-=-");
	}
	//異常通知
	public void throwsMethod(){
		System.out.println("異常通知執行");
	}
	//帶返回值的通知
	public void afterRuning(){
		System.out.println("=========返回值========");
	}
}

使用pojo-xml aspectj最重要的一個檔案,spring相關的配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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,實際是aspectjs自動掃描所有bean,去找有切面的bean -->
	<bean id="adviceService"
		class="www.csdn.spring.proxy.advice.aspectjs.pojoxml.AdviceService" />

	<!-- 真實主題 目標物件 -->
	<bean id="userServiceImpl"
		class="www.csdn.spring.proxy.advice.aspectjs.pojoxml.UserServiceImpl" />


	<!-- 使用pojo-xml aspectjs配置自動代理 -->
	<aop:config>
	
		<!-- 配置切面 -->
		<aop:aspect ref="adviceService">
		
			<!-- 引入通知 -->
			<aop:declare-parents types-matching="*..*Service*"
				implement-interface="www.csdn.spring.proxy.advice.aspectjs.pojoxml.Auditable"
				default-impl="www.csdn.spring.proxy.advice.aspectjs.pojoxml.AuditableImpl" />

			<!-- 切面切入的位置 切入點,可以同時寫多個不同的切入點 -->
			<aop:pointcut expression="execution(* www.csdn..UserServiceImpl.save(..))"
				id="myPcut" />
			<aop:pointcut expression="execution(* www.csdn..UserServiceImpl.update(..))"
				id="myPcuts" />

			<!--織入通知 method:指明方法; pointcut-ref引入切入點 -->
			<aop:before method="beforeMethod" pointcut-ref="myPcut" />
			<aop:after method="afterMethod" pointcut-ref="myPcut" />
			<aop:after method="afterRuning" pointcut-ref="myPcut" />
			<aop:around method="aroundMethod" pointcut-ref="myPcuts" />
			<aop:after-throwing method="throwsMethod" pointcut-ref="myPcuts"/>
		</aop:aspect>
		
	</aop:config>

</beans>


 

測試類 AdviseTest.java

package www.csdn.spring.proxy.advice.aspectjs.pojoxml;

import java.util.Date;

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

public class AdviseTest {

	@Test
	public void testUser() {

		ApplicationContext context = new ClassPathXmlApplicationContext(
				"spring-pojo*.xml");
		// 代理主題角色,這裡用代理主題和真實主題實現的同樣介面類來接收建立的代理主題,就是建立一個目標類
		UserService userService = context.getBean("userServiceImpl",
				UserService.class);
		
		userService.save(null);
		userService.update(null);
		
		//引入通知測試
		Auditable auditable = (Auditable) userService;
		auditable.setDate(new Date());
		System.out.println(auditable.getDate());
	}

}


 

相關文章