1. 程式人生 > >Spring AOP 初步

Spring AOP 初步

1.   面向切面程式設計(AOP)的概念

參見如下連結:

簡單地說,AOP就是在程式的縱向流程上新增橫向的切面邏輯,相當於給已有的業務邏輯增加額外的功能,而不改動原有的程式碼。

2.   Spring中的AOP流程

完整的AOP要素包括:

切面(Aspect),邏輯(Advice),連線點(Joinpoint),切入點(Pointcut),指令(introduction)目標(Target),代理(Proxy),插入(Weaving)

但是在Spring框架中,AOP的部分要素已經被實現好了,只有如下的流程需要開發者來補充完整,就像IoC一樣:

Advice →Pointcut →Proxy

3.   Advice

Spring中提供了四種類型的Advice,分別是:前置、後置、攔截、異常。

實現這四種類型的Advice分別需要實現下列四個介面:

前置:MethodBeforeAdvice,目標方法呼叫前執行

後置:AfterReturningAdvice,目標方法呼叫後執行

攔截:MethodInterceptor,目標方法在呼叫前被攔截

異常:ThrowsAdvice,目標方法丟擲異常時執行

上述Advice在配置xml時只需要指定bean的id和class即可,無特殊要求。

4.   Pointcut

預設下,Pointcut是不需要設定的。上述四種Advice對它們所作用的目標物件的每個方法都會被呼叫。但實際應用中,可能不需要每個方法都呼叫AOP的邏輯,因此可以指定Pointcut來限制Advice作用的方法。

雖然Spring提供了完整介面可供開發者自定義Pointcut,但是也提供更為簡便的內建類來簡化開發。主要有如下幾種:

1)       NameMatchMethodPointcut

繼承自NameMatchMethodPointcut類的切入點,可以在Advice執行之前自動匹配在切入點中設定好的方法名,目標物件中只有指定的方法才會呼叫Advice。

繼承了NameMatchMethodPointcut的Pointcut類必須覆蓋它的matches方法,並且在其中呼叫它的setMappedName(StringmethodName)來指定要攔截的方法名。methodName字串還可使用*萬用字元。

實現後的Pointcut類必須通過DefaultPointcutAdvisor進行裝配,以完成Advice的插入(Weaving)。詳見示例。

2)       RegexpMethodPointcutAdvisor

這種切入點使用Spring中內建的通過正則式來匹配方法名的切入點類,只需要在配置檔案中裝配RegexpMethodPointcutAdvisor,並在裝配的過程在屬性名為patterns(或pattern)的property中指定需要匹配的正則式即可。

3)       ControlFlowPointcut

與上述兩個類相比,ControlFlowPointcut(控制流切入點)的目標更為精細。它也是通過Spring中內建的控制流切入點類實現,並不需要編寫額外的切入點類。

在配置的時候,首先需要配置ControlFlowPointcut,並向它的構造方法中傳入兩個引數,第一個(index=0)為Advice所用的類名,第二個(index=1)為Advice作用的方法名。

之後,再配置DefaultPointcutAdvisor實現Advice的插入。

5.   Proxy

可以看出,在Spring中,AOP的要素是逐層插入(Weaving)的,即Advice插入Pointcut,而Pointcut也需要插入Proxy,以實現代理對目標類以及切面邏輯的共同呼叫。

在Spring,對Proxy的插入只需要配置ProxyFactoryBean即可,在其內部進一步配置Advice列表(list)和目標類(target),以及代理呼叫目標類的介面。其過程與Servlet中配置攔截器頗為相似。詳見示例。

6.   示例

前置Advice
package aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class MethodBeforeAdviceImpl implements MethodBeforeAdvice {

	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		System.out.println("=====前置開始=====");
		System.out.println("類名:" + target.getClass().getName() 
				+ "\t方法名:" + method.getName());
		System.out.print("引數列表:");
		for (Object arg: args) {
			System.out.print(arg + ", " + arg.getClass().getName() + ";\t");
		}
		args[0] = 123;
		System.out.println();
		System.out.println("將第一個引數改為123");
		System.out.println("=====前置結束=====");
	}

}

後置Advice
package aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterReturningAdviceImpl implements AfterReturningAdvice {

	public void afterReturning(Object returnValue, Method method, 
			Object[] args, Object target) throws Throwable {
		System.out.println("~~~~~後置開始~~~~~");
		System.out.println("返回值為:" + returnValue + ", " 
				+ returnValue.getClass().getName());
		System.out.print("將第二個引數改為345並再呼叫一次:");
		args[1] = 345;
		System.out.println(method.invoke(target, args));
		System.out.println("~~~~~後置結束~~~~~");
	}

}

攔截Advice
package aop.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MethodInterceptorImpl implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("*****攔截開始*****");
		if (invocation.getArguments()[1] instanceof Integer) {
			System.out.println("若第二個引數為整型則強制設定結果為-100");
			System.out.println("*****攔截結束*****");
			return -100;
		}
		System.out.println("*****攔截結束*****");
		
		return invocation.proceed();
	}

}

異常Advice
package aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;

public class ThrowsAdviceImpl implements ThrowsAdvice {

	public void afterThrowing(Throwable e) {
		System.out.println("-----異常開始-----");
		System.out.println(e.getClass().getName() + "\t" + e.getMessage());
		System.out.println("-----異常結束-----");
	}
	
	// ThrowsAdvice只是標識介面,實現類中必須至少實現兩個不同簽名的afterThrowing方法中的一個
//	public void afterThrowing(Method method, Object[] args, Object target, Throwable e) {
//		
//	}
}

按方法名匹配的Pointcut
package aop.pointcut;

import java.lang.reflect.Method;

import org.springframework.aop.support.NameMatchMethodPointcut;

public class NameMatchMethodPointcutImpl extends NameMatchMethodPointcut {

	private static final long serialVersionUID = -3944947836761348760L;

	public boolean matches(Method method, Class targetClass) {
		this.setMappedName("divide");
//		this.setMappedNames(new String[]{"add", "divide"});
		
		return super.matches(method, targetClass);
	}

}

目標類及其介面
package aop.service;

public interface IMath {
	public int add(int a, int b);
	public double add(double a, double b);
	public double divide(double a, double b);
}

package aop.service;

public class Math implements IMath {
	public int add(int a, int b) {
		return a + b;
	}
	
	public double add(double a, double b) {
		return a + b;
	}

	public double divide(double a, double b) throws IllegalArgumentException {
		if (b == 0) {
			throw new IllegalArgumentException("除數不能為0");
		}
		return a / b;
	}
}

配置檔案
<?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:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<bean id="math" class="aop.service.Math" />
	
	<bean id="before" class="aop.advice.MethodBeforeAdviceImpl" />
	<bean id="after" class="aop.advice.AfterReturningAdviceImpl" />
	<bean id="exception" class="aop.advice.ThrowsAdviceImpl" />
	<bean id="intercept" class="aop.advice.MethodInterceptorImpl" />
	
	<bean id="methodNamePointcut" class="aop.pointcut.NameMatchMethodPointcutImpl" />
	<bean id="nameAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="pointcut">
			<ref bean="methodNamePointcut" />
		</property>
		<property name="advice">
			<ref bean="before" />
		</property>
	</bean>
	
	<bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="pattern">
			<value>aop.service.IMath.divide</value>
		</property>
		<property name="advice">
			<ref bean="after" />
		</property>
	</bean>
	
	<bean id="cfPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
		<constructor-arg>
			<value>aop.Main</value>
		</constructor-arg>
		<constructor-arg>
			<value>add</value>
		</constructor-arg>
	</bean>
	<bean id="cfAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="pointcut">
			<ref bean="cfPointcut" />
		</property>
		<property name="advice">
			<ref bean="intercept" />
		</property>
	</bean>
	
	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="proxyInterfaces">
			<list>
				<value>aop.service.IMath</value>
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<!-- <value>after</value> -->
				<value>regexpAdvisor</value>
				<!-- <value>before</value> -->
				<value>nameAdvisor</value>
				<value>exception</value>
				<!-- <value>intercept</value> -->
				<value>cfAdvisor</value>
			</list>
		</property>
		<property name="target">
			<ref bean="math" />
		</property>
	</bean> 
</beans>

執行入口
package aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import aop.service.IMath;

public class Main {

	public static void main(String[] args) {
		ApplicationContext context = new FileSystemXmlApplicationContext(
				"src\\applicationContext.xml");
		IMath m = (IMath)context.getBean("proxy");
		System.out.println(add(m, 1, 2));
		System.out.println("..........................................................................................");
		System.out.println(m.add(4.0, 5.5));
		System.out.println("..........................................................................................");
		System.out.println(m.divide(10, 2));
		System.out.println("..........................................................................................");
		System.out.println(m.divide(5, 0));
	}

	public static int add(IMath m, int a, int b) {
		return m.add(a, b);
	}
}

7.   說明

1)       加入AOP之後,方法的執行順序是:

前置Advice →攔截Advice →目標方法 → 後置Advice

   若目標方法中丟擲了異常,則立即跳轉執行異常Advice,再跳轉回來執行異常處理(try…catch或throws)

2)       前置Advice和攔截Advice都能改動目標方法的引數值,區別在於,執行前置Advice之後一定要指定目標方法,而攔截Advice能夠阻止目標方法的執行;只有在攔截Advice中執行了invocation.proceed()方法才會執行目標方法,其返回值就是目標方法的返回值

3)       因為後置Advice是在目標方法返回(return)之後才執行,因此它不能對目標方法產生影響,但是可以通過反射機制的Method得知目標方法的一些資訊

4)       異常Advice實現的ThrowsAdvice介面只是一個標識介面,需要手動實現afterThrowing方法兩個簽名中的其中一個

5)       代理的攔截列表(interfaceNames)可以介面兩種型別的邏輯:Advice和Advisor。後者即是前述的自定義Pointcut的Advice;Spring中提供了許多介面可供實現Advisor,這裡只用到了較常用的幾個內建比較完整的抽象類

6)       攔截列表(interfaceNames)的順序不影響Advice/Advisor執行的順序

7)       完成AOP配置之後,在生成bean時需要用代理的id來替代目標類的id,即如:

IMath m = (IMath)context.getBean("proxy"); // 而不是用目標類的id:math

8)       代理的配置中必須在proxyInterfaces屬性中指定代理和目標類共用的介面的全名


相關推薦

Spring AOP初步總結(一)

RoCE 實現 rman eight 實施 code 時間 前置 font 學習AOP有段時間了,一直沒空總結一下,導致有些知識點都遺忘了,之後會把以前學過的Spring核心相關的知識點總結一輪... 先大體介紹下Spring AOP的特點(均摘自"Spring in

Spring AOP 初步

1.   面向切面程式設計(AOP)的概念 參見如下連結: 簡單地說,AOP就是在程式的縱向流程上新增橫向的切面邏輯,相當於給已有的業務邏輯增加額外的功能,而不改動原有的程式碼。 2.   Spring中的AOP流程 完整的AOP要素包括: 切面(Aspect),邏輯(A

【Java】 Spring 框架初步學習總結(一)簡單實現 IoC 和 AOP

1.0 其中 表示 只需要 第一篇 否則 info fin pojo   Spring 是一個開源的設計層面的輕量級框架,Spring 的好處網上有太多,這裏就不在贅述。   IoC 控制反轉和 AOP 面向切面編程是 Spring 的兩個重要特性。   IoC(Inver

Spring AOP初步總結(二)

該篇提供了一個小的AOP應用案例:系統日誌   @Aspect @Component public class SysLogAspect { @Autowired private SysLogService sysLogService; @Pointcut

初步學習Spring Aop使用之配置方式

前言:   初步學習接觸Spring框架使用,而在這裡對自己剛學的Aop使用做個小篇幅的總結,方便日後需要用到是可以快速入手!   僅用於對自己學習個人筆記,不做任意分享,純屬個人理解不想誤認子弟!   一、Aop知識點理解   AOP稱為面向切面程式設計,在程式開發中主要用來解決一些系統

初步學習Spring Aop使用之註解方式

前言: 這裡就主要演示Spring中Aop使用註解是怎麼使用,如果需要了解更多Aop相關概念,可檢視相關資料的介紹 一、專案目錄  【標記檔案為主要檔案】 二、各個檔案的程式碼 AopServer.java  【編寫切點的檔案,就是一些需要被修飾的業務處理】 package aop; i

Spring AOP 實現原理

pri ack more .net style 實現原理 cor http details Spring AOP 實現原理Spring AOP 實現原理

Spring Aop Annotation

ava rgs implement star nco pos over value resource 實體: 1 package com.bxw.aop.vo; 2 3 public class User { 4 private String logi

Spring AOP實驗之靜態代理

註入 ans 執行 軟件開發 動態代理 sin modified 實現 編譯 1 AOP的概念和原理 1.1 AOP原理   AOP(Aspect Oriented Programming),也就是面向方面編程的技術。AOP基於IoC基礎,是對OOP的有益補充。   

深入理解Spring AOP之二代理對象生成

gets code 網上 none work class als post 產生 深入理解Spring AOP之二代理對象生成 spring代理對象 上一篇博客中講到了Spring的一些基本概念和初步講了實現方

Spring AOP之 動態代理實例

delete 日誌 實現類 imp exc print cati user ins 1.項目結構圖如下3.3.3.1: 圖3.3.3.1 2.IUserServ接口代碼與UserServImpl實現類代碼和上述代碼相同 3.LogHandler類代碼

Spring Aop總結

join spring容器 其他 新的 conf 註解 基於 prop 參數 一.Spring介紹 Spring它是一個一站式的分層輕量級框架1.Spring體系結構 1.core container a) beans與core他們提供sping框架最基本的功能,它包含i

spring---aop(3)---Spring AOP的攔截器鏈

ati handler 攔截器 odi hand zab chain 記載 封裝 寫在前面   時間斷斷續續,這次寫一點關於spring aop攔截器鏈的記載。至於如何獲取spring的攔截器,前一篇博客已經寫的很清楚(spring---aop(2)---Spring AO

Spring AOP詳解

開發 blank 關註 proxy 系統 frame main函數 lns 功能 一.前言 在以前的項目中,很少去關註spring aop的具體實現與理論,只是簡單了解了一下什麽是aop具體怎麽用,看到了一篇博文寫得還不錯,就轉載來學習一下,博文地址:htt

Spring--AOP 例子

結束 oaf signature 日誌 類加載器 lis 能說 ssi system 先用代碼講一下什麽是傳統的AOP(面向切面編程)編程 需求:實現一個簡單的計算器,在每一步的運算前添加日誌。最傳統的方式如下: Calculator.Java [java] vie

Spring AOP @Before @Around @After 等 advice 的執行順序

繼續 case tco ann cto order software clas spec 用過spring框架進行開發的人,多多少少會使用過它的AOP功能,都知道有@Before、@Around和@After等advice。最近,為了實現項目中的輸出日誌和權限控制這兩個需求

spring AOP

切入點 動作 3.1 插入 3.2 implement bject version com 本節要點: 掌握AOP概念 掌握AOP的有關術語 掌握spring AOP框架的實現方式 在文章“spring靜態代理和動態代理”中演示了如何使用jdk動態代理功能實現一個最

Spring技術內幕:Spring AOP的實現原理(三)

dede ide configure ida mini == src min dem 生成SingleTon代理對象在getSingleTonInstance方法中完畢,這種方法時ProxyFactoryBean生成AopProxy對象的入口。代理對象會

spring-aop

private 我們 數據 pri evaluate users ica led 設計 聊spring-aop之前,先來看一下Aspectj的使用 Aspectj 從http://www.eclipse.org/aspectj/downloads.php下載好aspectj

spring-AOP之通知和顧問

多個 targe ges 配置 context color ive 後置 功能 通知和顧問都是切面的實現形式,其中通知可以完成對目標對象方法簡單的織入功能。 而顧問包裝了通知,可以讓我們對通知實現更加精細化的管理,讓我們可以指定具體的切入點。 通知分為前置通知,環繞通知及後