spring配置總結——aop配置部分

接上篇beans部分,這部分說下aop的配置。

AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論。是對傳統OOP的補充。

AOP的主要編程對象是切面(aspect),而切面模塊化橫切點。

在應用AOP編程時,仍需要定義公共功能,但可以明確的定義這個功能在哪裏,以什麼方式應用,並且不必修改受影響的類,這樣一來橫切關注點就被模塊化到特殊的獨享(切面)裏。

AOP的好處:

    每個業務邏輯唯一一個位置,代碼不分散,便於維護和升級。

    業務模塊簡潔,值包含核心業務代碼。


AOP術語:

    切面(Aspect):橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象。

    通知(Advice):切面必須要完成的工作。

    目標(Target):被通知的對象。

    代理(Proxy):向目標對象應用通知之後創建的對象。

    連接點(JoinPoint):程序執行的某個特定位置。如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。例如ArithmeticCalculator#add()方法執行前的連接點:執行點爲ArithmeticCalculator#add()方位爲該方法執行前的位置。

    切點(pointcut):每個類都擁有多個連接點。例如:ArithmeticCalculator的所有方法實際上都是連接點,即連接點是程序類中可觀存在的事物。AOP通過切點定位到特點的連接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。起點和連接點不是一對一的關係,一個切點匹配多個連接點,切點通過org.springframeword.Pointcut接口描述,它使用類和方法作爲連接點的查詢條件。

AspectJ:Java社區中最完整最流行的AOP框架。

在Spring中啓用AspectJ註解支持;

要在Spring應用中應用AspectJ註解,必須在classpath下包含AspectJ類庫:aopalliance.jar、aspectj.weaver.jar和Spring-aspects.jar.

將aop scheme添加到<beans>根元素中。

要在SpringIOC容器中啓用AspectJ註解支持,只要在Bean配置文件中定義一個空的XM元素<aop:aspectj-autoproxy>

當SpringIOC容器偵測到Bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動與AspectJ切面匹配的Bean創建代理。

用AspectJ註解聲明切面

要在Spring中聲明AspectJ切面,只需要在IOC容器中將切面生面爲Bean實例,當在SpringIOC容器中初始化AspectJ切面之後,SpingIOC容器就會爲那些與AspectJ切面相匹配的Bean創建代理。

在AspectJ註解中。切面只是一個帶有@Aspect註解的Java類。

通知是標註有某種註解的簡單的java方法。

AspectJ支持5種類型的通知註解:

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

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

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

    @AfterThrowing:異常通知,在方法拋出異常之後執行

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

利用方法簽名編寫AspectJ切入點表達式

最典型的切入點表達式是根據犯法的簽名來匹配各種方法:

    execution * com.atguigu.spring.ArithmeticCalculator.*(...):匹配ArithmeticCalculator中聲明的所有方法,第一個*表示任意修飾符機返回值。第二個*代表任意方法。...匹配任意數量的參數,若目標類與接口與接口與該切面在一個包中,可以省略包名。

    execution public * com.atguigu.spring.ArithmeticCalculator.*(...):匹配ArithmeticCalculator接口中所有public的方法。

    execution public double com.atguigu.spring.ArithmeticCalculator.*(...):匹配ArithmeticCalculator接口中返回double類型的方法

    execution public double com.atguigu.spring.ArithmeticCalculator.*(.double, ..):匹配第一個參數爲double類型的方法,..匹配任意數量任意類型的參數。

    execution public double com.atguigu.spring.ArithmeticCalculator.*(double, double):匹配參數類型爲double,double類型的方法。

在AspectJ中,切入點表達式可以通過操作符&&,||,!結合起來。




可以在通知方法中聲明一個類型爲JoinPoint的參數。然後就能訪問連接細節。如方法名和參數值。

對於環繞通知來說連接點的參數類型必須是ProceedingJoinPoint。他是JoinPoint的子接口,允許控制何時執行,是否執行連接點。

在環繞通知中,需要明確調用ProceedingJoinPoint的proceed()方法來執行被代理的方法,如果忘記這樣做就會導致通知被執行了,但是目標方法沒有被執行。

注意:環繞通知的方法需要返回目標方法執行之後的結果,即調用jointPoint.proceed()的返回值,否則會出現空指針異常。

指定切面優先級: 可以使用@Order註解來指定切面的優先級,值越小優先級越高

重用切入點定義:通過@Pointcut註解將一個切入點聲明稱簡單的方法,切入點的方法體通常是空的。切入點方法的訪問控制同時也控制着這個切入點的可見性。如果切入點要在多個切面找那個共用,最好將他們幾種在一個公共類中。在引入切入點時,必須將類名也包括在內,如果類沒有與這個切面放在同一個包中,還必須包含包名。

基於註解的aop示例:

public interface ArithmeticCalculator {
	int add(int i ,int j);
	int sub(int i,int j );
	int mul(int i,int j);
	int div (int i,int j);
}
@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public int add(int i, int j) {
		int result = i + j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result = i - j;
		return result;
	}

	@Override
	public int mul(int i, int j) {
		int result = i * j;
		return result;
	}

	@Override
	public int div(int i, int j) {
		int result = i / j;
		return result;
	}

}
@Order(1)
@Component
@Aspect
public class ValidationAspect {
	/*@Before("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(int, int))")*/
	@Before("LoggingAspect.declareJoinPoint()")
	public void validate(JoinPoint joinPoint){
		System.out.println("--> validation" + Arrays.asList(joinPoint.getArgs()));
	}
}
package com.atguigu.spring.aop.impl;

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

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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註解來指定切面的優先級,值越小優先級越高
 * @author adminitrator
 *
 */
@Order(2)
@Component
@Aspect
public class LoggingAspect {
	/**
	 * 定義一個方法,用於聲明切入點表達式,一般地,該方法中不需要添加其他代碼
	 * 使用@Pointcut註解來聲明切入點表達式
	 * 後面的其他同志直接使用方法名來引用當前的額切入點表達式。
	 */
	@Pointcut("execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(int, int))")
	public void declareJoinPoint(){
		
	}
	
	//聲明該方法是一個前置通知:在目標方法之前執行
		//註解中的* 表示ArithmeticCalculator接口下的所有方法
		//註解參數中的 public int 也可也用* 來代替,表示任意修飾符,任意返回類型
		@Before("declareJoinPoint()")
		public void beforeMethod(JoinPoint joinPoint){//joinPoint 連接點
			String  methodName = joinPoint.getSignature().getName();
			List<Object> args = Arrays.asList(joinPoint.getArgs());
			System.out.println("The method "+ methodName+" begins with "+ args);
		}
		
		//後置通知:在目標方法執行之後(無論是否發生異常),執行的通知
		//後置通知中還不能訪問目標方法執行的結果
		@After(value = "declareJoinPoint()")
		public void afterMethod(JoinPoint joinPoint){
			String  methodName = joinPoint.getSignature().getName();
			List<Object> args = Arrays.asList(joinPoint.getArgs());
			System.out.println("The method "+ methodName+" begins with "+ args);
		}
		
		//在方法正常返回結束後執行的代碼
		//放回通知是可以範文到方法的返回值的
		@SuppressWarnings("unused")
		@AfterReturning(value = "execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(int, int))",
				returning="result")
		public void afterReturning(JoinPoint joinPoint,Object result){
			String  methodName = joinPoint.getSignature().getName();
			List<Object> args = Arrays.asList(joinPoint.getArgs());
			System.out.println("The method "+ methodName+ " result-->" +result);
		}
		//異常通知
		/**
		 * 在目標方法出現異常時,會執行代碼
		 * 可以方位到異常對象;可以指定出現特定異常時在執行通知。通諾throwing參數中異常,在方法參數中執行異常類型。
		 * @param joinPoint
		 * @param ex
		 */
		@SuppressWarnings("unused")
		@AfterThrowing(value = "execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(int, int))",throwing="ex")
		public void afterThrowing(JoinPoint joinPoint,Exception ex){
			String  methodName = joinPoint.getSignature().getName();
			List<Object> args = Arrays.asList(joinPoint.getArgs());
			System.out.println("The method "+ methodName +" occurs exception " + ex);
		}
	
	//環繞通知 就相當於動態代理
	/**
	 * 環繞通知需要攜帶ProceedingJoinPoint類型參數
	 * 環繞通知類似於動態代理的全過程:ProceedingJoinPoint 類型的參數可以決定是否執行目標方法
	 * 且環繞通知必須有返回值,返回值即爲目標方法的返回值。
	 * @param joinPoint
	 */
/*	@Around(value = "execution(public int com.atguigu.spring.aop.impl.ArithmeticCalculator.*(int, int))")
	public Object aroundMethod(ProceedingJoinPoint pjd){
		//執行目標方法
		Object result = null;
		try {
			System.out.println("The method " + pjd.getSignature().getName()+ " begin " + Arrays.asList(pjd.getArgs()));
			result = pjd.proceed();
			//後置通知
			System.out.println("The method " + pjd.getSignature().getName()+ " ends  " +Arrays.asList(pjd.getArgs()));
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			//異常通知
			System.out.println("The method " + pjd.getSignature().getName()+ " occurs " + e);
			throw new RuntimeException(e);
		}
		//後置通知
		System.out.println("The method " + pjd.getSignature().getName()+ " ends");
		return result;
	}*/
}
<?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"
	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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!-- 配置自動掃描的包 -->
	<context:component-scan base-package="com.atguigu.spring.aop.impl" />

	<!-- 這句話的作用是使我們在切面聲明的註解(如:before)起作用:在我調用目標方法跟before註解中配置的方法相匹配的時候,aop自動爲那個目標方法所在的類生成代理對象,在調用方法之前執行配置的方法。 -->
	<!-- 使AspectJ註解起作用:自動爲匹配的類生成代理對象 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

基於XML的AOP配置:

public interface ArithmeticCalculator {
	int add(int i ,int j);
	int sub(int i,int j );
	int mul(int i,int j);
	int div (int i,int j);
}
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	@Override
	public int add(int i, int j) {
		int result = i + j;
		return result;
	}

	@Override
	public int sub(int i, int j) {
		int result = i - j;
		return result;
	}

	@Override
	public int mul(int i, int j) {
		int result = i * j;
		return result;
	}

	@Override
	public int div(int i, int j) {
		int result = i / j;
		return result;
	}

}
public class LoggingAspect {

	public void declareJoinPoint() {

	}

	public void beforeMethod(JoinPoint joinPoint) {// joinPoint 連接點
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method " + methodName + " begins with " + args);
	}

	public void afterMethod(JoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method " + methodName + " begins with " + args);
	}

	@SuppressWarnings("unused")
	public void afterReturning(JoinPoint joinPoint, Object result) {
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method " + methodName + " result-->" + result);
	}

	@SuppressWarnings("unused")
	public void afterThrowing(JoinPoint joinPoint, Exception ex) {
		String methodName = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("The method " + methodName + " occurs exception " + ex);
	}

	public Object aroundMethod(ProceedingJoinPoint pjd) {
		// 執行目標方法
		Object result = null;
		try {
			System.out.println("The method " + pjd.getSignature().getName() + " begin " + Arrays.asList(pjd.getArgs()));
			result = pjd.proceed();
			// 後置通知
			System.out.println("The method " + pjd.getSignature().getName() + " ends  " + Arrays.asList(pjd.getArgs()));
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			// 異常通知
			System.out.println("The method " + pjd.getSignature().getName() + " occurs " + e);
			throw new RuntimeException(e);
		}
		// 後置通知
		System.out.println("The method " + pjd.getSignature().getName() + " ends");
		return result;
	}
}
public class ValidationAspect {

	public void validate(JoinPoint joinPoint){
		System.out.println("--> validation" + Arrays.asList(joinPoint.getArgs()));
	}
}
<?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"
	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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	<!-- arithmeticCalculatorImpl 納入IOC容器管理 -->
	<bean id="arithmeticCalculatorImpl" class="com.atguigu.spring.aop.xml.ArithmeticCalculatorImpl"></bean>
	
	<!-- 配置切面的Bean -->
	<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>
	<bean id="validationAspect" class="com.atguigu.spring.aop.xml.ValidationAspect"></bean>
	
	<!--配置aop  -->
	<aop:config>
		<!-- 配置切點表達式 -->
		<aop:pointcut expression="execution(* com.atguigu.spring.aop.xml.ArithmeticCalculator.*(int, int))" id="pointcut"/>
		<!-- 配置切面和通知 -->
		<aop:aspect ref="loggingAspect" order="2" >
			<!-- <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
			<aop:after method="afterMethod" 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="aroundMethod" pointcut-ref="pointcut" />
		</aop:aspect>
		<aop:aspect ref="validationAspect" order="1">
			<aop:before method="validate" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>
</beans>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章