1. 程式人生 > >Spring學習(穀粒學院spring4課程)第六節 AOP

Spring學習(穀粒學院spring4課程)第六節 AOP

一:採用AOP的原因

程式碼混亂:越來越多的非業務需求(日誌和驗證等)加入後, 原有的業務方法急劇膨脹.  每個方法在處理核心邏輯的同時還必須兼顧其他多個關注點.

程式碼分散: 以日誌需求為例, 只是為了滿足這個單一需求, 就不得不在多個模組(方法)裡多次重複相同的日誌程式碼. 如果日誌需求發生變化, 必須修改所有模組.

二:基於aspectj註解宣告切面

例項:定義一個bean,為其新增日誌

(1)定義一個bean

import org.springframework.stereotype.Component;

@Component
public class Student {
private String name;
public void setName(String name) {
	this.name = name;
}
public int study(int i,int j) {
	// TODO Auto-generated method stub
System.out.println("good study");
return i/j;
}
}

(2)建立切面及通知

package com.wh.spring.impl;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
//把這個類宣告為一個切面,需要把該類放入到IOC容器中,再宣告為一個切面
//使用@Order指定切面的優先順序,數值越小,優先順序越高
@Aspect
@Order(1)
@Component
public class LoggingAspect {
	
	//定義一個方法,用於宣告切入點表示式,一般該方法不需要填入其他程式碼
	//使用@Pointcut宣告切入點表示式
	//其他通知引用方法名引入該切入點
	@Pointcut("execution(* com.wh.spring.aop.Student.*(..))")
	public void declareJointPointExpression() {
		
	}
    //宣告該方法是一個前置通知
	//spring..表示com.wh.spring包及其子包
	@Before("declareJointPointExpression()")
	public void beforeMethord(JoinPoint joinPoint) {
		String methordName=joinPoint.getSignature().getName();
		List<Object> args=Arrays.asList(joinPoint.getArgs());
		System.out.println("the methord before "+methordName+" "+args);
	}
	//後置通知:在目標方法執行後(無論是否發生異常),執行的通知
	//後置通知中不能訪問目標方法的執行結果
	@After("declareJointPointExpression()")
	public void afterMethord(JoinPoint joinPoint) {
		String methordName=joinPoint.getSignature().getName();
		System.out.println("The methord "+methordName+" ends");
		
	}
	
	//返回通知,方法正常結束後執行的程式碼
	//返回通知是可以訪問到方法的返回值
	@AfterReturning(value="declareJointPointExpression()",
			returning="result")
	public void afterReturning(JoinPoint joinPoint,Object result) {
		String methordName=joinPoint.getSignature().getName();
		System.out.println("The methord "+methordName+" ends with "+result);
	}
	
	
	//在目標方法出現異常時會執行該程式碼,可以訪問到異常物件,且可以指定在出現指定異常時才執行通知程式碼
	//指定ex的型別
	@AfterThrowing(value="declareJointPointExpression()",
			throwing="ex")
	public void afterThrowing(JoinPoint joinPoint,Exception ex) {
		String methordName=joinPoint.getSignature().getName();
		System.out.println("The methord "+methordName+" throw "+ex);
	}
	
	
	//環繞通知需要攜帶ProceedingJoinPoint型別的引數
	//環繞通知類似於動態代理的全過程:ProceedingJoinPoint型別的引數可以決定是否執行目標方法,且環繞通知必須有返回值
	//返回值即為目標方法的返回值
	//不常用環繞通知
	@Around("declareJointPointExpression()")
	public Object aroundMethord(ProceedingJoinPoint pjd) {
		System.out.println("aroundMethord");
		Object result=null;
		String methordName=pjd.getSignature().getName();
		//執行目標方法 
		try {
			//前置通知
			System.out.println(methordName+" begins with "+Arrays.asList(pjd.getArgs()));
			result=pjd.proceed();			
			//返回通知
			System.out.println(methordName+" ends with "+result);
			
		} catch (Throwable e) {
			// 異常通知
			System.out.println("發生異常:"+e);
		}
       // 後置通知
		System.out.println("the methord "+methordName+" ends with around");
		return 100;
	}
	
}

(3)使aspectj註解起作用,自動為匹配的類生成代理物件

在spring配置檔案中加入

<!-- 使aspectj註解起作用,自動為匹配的類生成代理物件 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

三:基於配置檔案的AOP

(1)配置切面bean

<!-- 配置切面的bean-->
<bean id="loggingAspect" class="com.wh.spring.xml.LoggingAspect"></bean>

(2)配置切點,切面及通知

<aop:config>
<!-- 配置切點表示式 -->
<aop:pointcut expression="execution(* com.wh.spring.xml.Student.*(..))" id="pointcut"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="0">
<aop:before method="beforeMethord" pointcut-ref="pointcut"/>
<aop:after method="afterMethord" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:around method="aroundMethord" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>

四:其他

(1)切面的順序由@Order(1)或order="0"指定,數字越小,優先順序越高

(2)切點的重用

//定義一個方法,用於宣告切入點表示式,一般該方法不需要填入其他程式碼
	//使用@Pointcut宣告切入點表示式
	//其他通知引用方法名引入該切入點
	@Pointcut("execution(* com.wh.spring.aop.Student.*(..))")
	public void declareJointPointExpression() {
		
	}