Spring -- AOP入門基礎&基於Aspect的AOP通知用法
動態代理
我們在日常開發過程中是否會遇到下圖中的這種狀況
紅框中的是我們要輸出的日誌,你是否發現,日誌中大部分資訊都是相同的,並且如果我們要修改一個地方,所有的地方都需要改,而且程式碼看起來還比較冗餘
下面我們就可以通過動態代理的方式解決這個問題
看下程式碼
public interface Calculation {
public int add(int x, int y);
public int sub(int x, int y);
public int mul(int x, int y);
public int dev(int x, int y);
}
定義介面,加減乘除方法。
public class CalculationImpl implements Calculation {
@Override
public int add(int x, int y) {
int result = x + y;
return result;
}
@Override
public int sub(int x, int y) {
int result = x - y;
return result;
}
@Override
public int mul(int x, int y) {
int result = x * y;
return result;
}
@Override
public int dev(int x, int y) {
int result = x / y;
return result;
}
}
具體實現類,這裡我們看到,沒有植入日誌。
public class CalculationProxy {
private Calculation calculation = null ;
CalculationProxy(Calculation calculation) {
this.calculation = calculation;
}
public Calculation getCalculationLog() {
Calculation proxy = null;
ClassLoader loader = calculation.getClass().getClassLoader();
Class[] interfaces = new Class[] { Calculation.class };
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out
.println("GP-->invoke begin , execute method is "
+ method.getName() + ", args is "
+ Arrays.asList(args));
Object obj = method.invoke(calculation, args);
return obj;
}
};
proxy = (Calculation) Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}
動態代理類的實現,在實現invoke方法的時候,我們輸出日誌在呼叫目標方法之前。
測試
public class Main {
public static void main(String[] args) {
Calculation calculation = new CalculationImpl();
CalculationProxy calculationProxy = new CalculationProxy(calculation);
Calculation cal = calculationProxy.getCalculationLog();
System.out.println(cal.add(1, 3));
System.out.println(cal.mul(1, 5));
}
}
輸出結果
GP–>invoke begin , execute method is add, args is [1, 3]
4
GP–>invoke begin , execute method is mul, args is [1, 5]
5
看起來是不是要清晰多了,我們將日誌單獨提取到動態代理的方法中,對核心的業務方法沒有侵入,而且還便於我們的維護。
AOP簡介
AOP(Aspect-Oriented Programming, 面向切面程式設計): 是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向物件程式設計) 的補充.
AOP 的主要程式設計物件是切面(aspect), 而切面模組化橫切關注點.
在應用 AOP 程式設計時, 仍然需要定義公共功能, 但可以明確的定義這個功能在哪裡, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關注點就被模組化到特殊的物件(切面)裡.
AOP 的好處:
- 每個事物邏輯位於一個位置, 程式碼不分散, 便於維護和升級
- 業務模組更簡潔, 只包含核心業務程式碼.
AOP術語
切面(Aspect): 橫切關注點(跨越應用程式多個模組的功能)被模組化的特殊物件
通知(Advice): 切面必須要完成的工作
目標(Target): 被通知的物件
代理(Proxy): 向目標物件應用通知之後建立的物件
連線點(Joinpoint):程式執行的某個特定位置:如類某個方法呼叫前、呼叫後、方法丟擲異常後等。連線點由兩個資訊確定:方法表示的程式執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連線點,執行點為 ArithmethicCalculator#add(); 方位為該方法執行前的位置
切點(pointcut):每個類都擁有多個連線點:例如 ArithmethicCalculator 的所有方法實際上都是連線點,即連線點是程式類中客觀存在的事務。AOP 通過切點定位到特定的連線點。類比:連線點相當於資料庫中的記錄,切點相當於查詢條件。切點和連線點不是一對一的關係,一個切點匹配多個連線點,切點通過 org.springframework.aop.Pointcut 介面進行描述,它使用類和方法作為連線點的查詢條件。
基於Aspect的AOP – 前置通知
涉及JAR包
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
spring-aop-4.1.7.RELEASE.jar
spring-aspects-4.1.7.RELEASE.jar
spring-beans-4.1.7.RELEASE.jar
spring-context-4.1.7.RELEASE.jar
spring-core-4.1.7.RELEASE.jar
spring-expression-4.1.7.RELEASE.jar
看下程式碼
public interface Calculation {
public int add(int x, int y);
public int sub(int x, int y);
public int mul(int x, int y);
public int dev(int x, int y);
}
@Component
public class CalculationImpl implements Calculation {
@Override
public int add(int x, int y) {
int result = x + y;
return result;
}
@Override
public int sub(int x, int y) {
int result = x - y;
return result;
}
@Override
public int mul(int x, int y) {
int result = x * y;
return result;
}
@Override
public int dev(int x, int y) {
int result = x / y;
return result;
}
}
實現介面,並且使用@Component標記為IOC容器中的元件。
<context:component-scan base-package="com.gp.spring.aop.impl"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
加入掃描註解的包路徑。
Spring預設不支援@AspectJ風格的切面宣告,增加aspectj-autoproxy,這樣Spring就能發現@AspectJ風格的切面並且將切面應用到目標物件。
@Aspect
@Component
public class CalculationAspect {
@Before("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
List<Object> list = Arrays.asList(joinPoint.getArgs());
System.out.println("Method begin ... ,method=" + name + ", args = "
+ list);
}
}
- CalculationAspect 標記為Component元件,註冊到IOC容器中
- @Aspect標記,spring對其進行AOP相關的配置,生成相應的代理類
- @Before,為前置通知,方法執行前的通知
- “execution(public int com.gp.spring.aop.impl.Calculation.add(int,
int))”表示式,表示此通知要執行的包路徑、方法的相關資訊。此表示式可進行模糊匹配,比如”execution(public int
com.gp.spring.aop.impl.Calculation.*(..))”,表示Calculation類下的所有方法。 - 方法中的引數JoinPoint joinPoint,表示連線點,通過此引數可以獲取到執行方法的相關資訊,比如方法名、方法引數。
執行測試方法
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Calculation calculation = (Calculation)context.getBean("calculationImpl");
int result = calculation.add(3, 4);
System.out.println(result);
}
輸出結果
Method begin … ,method=add, args = [3, 4]
7
基於Aspect的AOP – 後置通知
後置通知與前置通知用法類似,區別就是在執行方法之後執行後置通知方法。
程式碼如下
@After("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
public void afterMethod(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
List<Object> list = Arrays.asList(joinPoint.getArgs());
System.out.println("Method end ... ,method=" + name + ", args = "
+ list);
}
這裡用的@After註解,其他都沒變化。
測試輸出結果如下
Method begin … ,method=add, args = [3, 4]
executeing …
Method end … ,method=add, args = [3, 4]
7
我在對add方法增加了一個輸出,便於區分前置通知、後置通知
public int add(int x, int y) {
int result = x + y;
System.out.println("executeing ...");
return result;
}
基於Aspect的AOP – 返回通知
方法執行成功後,呼叫返回通知,如果方法在執行過程中丟擲異常,則不會呼叫。
程式碼
@AfterReturning(value = "execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))", returning = "ret")
public void afterReturnMethod(JoinPoint joinPoint, Object ret) {
String name = joinPoint.getSignature().getName();
List<Object> list = Arrays.asList(joinPoint.getArgs());
System.out.println("@AfterReturning ... ,method=" + name + ", args = "
+ list + ", return = " + ret);
}
與後置通知不同的是,後置通知在方法丟擲異常,仍會被呼叫,這裡我們可以使用try … catch … 進行類比。
輸出結果
@Before … ,method=add, args = [3, 4]
executeing …
@After … ,method=add, args = [3, 4]
@AfterReturning … ,method=add, args = [1, 3], return = 4
7
基於Aspect的AOP – 異常通知
方法在執行過程中,如果丟擲異常,則呼叫異常通知
程式碼
@AfterThrowing(value = "execution(public int com.gp.spring.aop.impl.Calculation.*(int, int))", throwing = "ex")
public void afterThrowMethod(JoinPoint joinPoint, Exception ex) {
System.out.println("@AfterThrowing ... ,ex = " + ex);
}
此處與之前的用法有些卻別,在註解的引數中增加了throwing,然後在方法中增加了要捕獲的異常,此處類似於try…catch…的catch中程式碼塊
輸出結果
Exception in thread “main” @AfterThrowing … ,ex = java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at com.gp.spring.aop.impl.CalculationImpl.dev(CalculationImpl.java:29)
基於Aspect的AOP – 環繞通知
環繞通知,類似與我們最開始講解的動態代理,在此通知中你可以去呼叫目標方法,並在目標方法的上下做各種通知(前置、返回、後置、異常等處理)
程式碼:
@Around("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
public Object aroundMethod(ProceedingJoinPoint pjd) {
String name = pjd.getSignature().getName();
List<Object> list = Arrays.asList(pjd.getArgs());
Object obj = null;
System.out.println("前置通知 ... ,method=" + name + ", args = "
+ list);
try {
obj = pjd.proceed();
System.out.println("返回通知 ... ,method=" + name + ", args = "
+ list);
} catch (Throwable e) {
System.out.println("異常通知 ... , exception = " + e);
e.printStackTrace();
}
System.out.println("後置通知 ... ,method=" + name + ", args = "
+ list);
return obj;
}
返回結果:
前置通知 … ,method=add, args = [1, 3]
executeing …
返回通知 … ,method=add, args = [1, 3]
後置通知 … ,method=add, args = [1, 3]
4
基於Aspect的AOP – 切入方法優先順序
下面我們在來增加一個前置通知,建立一個新類
程式碼如下
@Aspect
@Component
public class ValidateAspect {
@Before("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
public void validate(){
System.out.println("驗證方法 com.gp.spring.aop.impl.ValidateAspect");
}
}
此類我們做了一個驗證。
然後執行測試方法(程式碼複用基於Aspect的AOP – 環繞通知)
前置通知 … ,method=add, args = [1, 3]
驗證方法 com.gp.spring.aop.impl.ValidateAspect
executeing …
返回通知 … ,method=add, args = [1, 3]
後置通知 … ,method=add, args = [1, 3]
@AfterReturning … ,method=add, args = [1, 3], return = 4
4
驗證方法 com.gp.spring.aop.impl.ValidateAspect,輸出了我們新增加的切入方法,那麼這裡的先後順序怎麼制定呢,我們可以使用@order(1)
如下
@Order(1)
@Aspect
@Component
public class ValidateAspect {
}
@Order(1)表示優先執行此切入方法。數值越小,優先順序越高。
基於Aspect的AOP – 重用切點表示式
我們之前學習過,定義一個切面方法,要在其註解中,增加切入的目標方法資訊,那如果多個方法都切入同樣的目標方法的時候,我們可不可以將相同的目標方法資訊提取出來呢。
下面我們演示下程式碼(複用之前程式碼)
@Order(2)
@Aspect
@Component
public class CalculationAspect {
@Pointcut("execution(public int com.gp.spring.aop.impl.Calculation.*(..))")
public void aspectName(){
}
@AfterReturning(value = "aspectName()", returning = "ret")
public void afterReturnMethod(JoinPoint joinPoint, Object ret) {
String name = joinPoint.getSignature().getName();
List<Object> list = Arrays.asList(joinPoint.getArgs());
System.out.println("@AfterReturning ... ,method=" + name + ", args = "
+ list + ", return = " + ret);
}
@Around("aspectName()")
public Object aroundMethod(ProceedingJoinPoint pjd) {
String name = pjd.getSignature().getName();
List<Object> list = Arrays.asList(pjd.getArgs());
Object obj = null;
System.out.println("前置通知 ... ,method=" + name + ", args = "
+ list);
try {
obj = pjd.proceed();
System.out.println("返回通知 ... ,method=" + name + ", args = "
+ list);
} catch (Throwable e) {
System.out.println("異常通知 ... , exception = " + e);
e.printStackTrace();
}
System.out.println("後置通知 ... ,method=" + name + ", args = "
+ list);
return obj;
}
}
我們定義了一個aspectName方法,然後增加@Pointcut(“execution(public int com.gp.spring.aop.impl.Calculation.*(..))”)註解。
然後後續,直接呼叫此方法名即可。
如果在其他類中呢,增麼呼叫aspectName方法,程式碼如下
@Order(1)
@Aspect
@Component
public class ValidateAspect {
@Before("com.gp.spring.aop.impl.CalculationAspect.aspectName()")
public void validate(){
System.out.println("驗證方法 com.gp.spring.aop.impl.ValidateAspect");
}
}
方法所在類的全路徑名即可。
執行結果
驗證方法 com.gp.spring.aop.impl.ValidateAspect
前置通知 … ,method=add, args = [1, 3]
executeing …
返回通知 … ,method=add, args = [1, 3]
後置通知 … ,method=add, args = [1, 3]
@AfterReturning … ,method=add, args = [1, 3], return = 4
4
相關推薦
Spring -- AOP入門基礎&基於Aspect的AOP通知用法
動態代理 我們在日常開發過程中是否會遇到下圖中的這種狀況 紅框中的是我們要輸出的日誌,你是否發現,日誌中大部分資訊都是相同的,並且如果我們要修改一個地方,所有的地方都需要改,而且程式碼看起來還比較冗餘 下面我們就可以通過動態代理的方式解決這個問題 看
基於代理的spring aop中,使用xml實現通知和引入
ProxyFactoryBean xml配置中,實現代理工廠的類 屬性 定義 target 代理的目標物件 proxyInterfaces 代理需要實現的
Spring batch 入門基礎
clas 環境 html 日誌 過程 容易 計劃 bre .html Spring Batch是一個輕量級的,完全面向Spring的批處理框架,可以應用於企業級大量的數據處理系統。Spring Batch以POJO和大家熟知的Spring框架為基礎,使開發者更容易的訪問和利
Spring AOP 入門
系統 add method schema main函數 方式 測試 對象 ng- 目錄 AOP概念 AOP核心概念 Spring對AOP的支持 基於Spring的AOP簡單實現 基於Spring的AOP使用其他細節 AOP概念 AOP(Aspect Oriented P
spring AOP切面程式設計——基於自定義註解
AOP稱為面向切面程式設計,在程式開發中主要用來解決一些系統層面上的問題,比如日誌,事務,許可權等待, (1)Aspect(切面):通常是一個類,裡面可以定義切入點和通知 (2)JointPoint(連線點):程式執行過程中明確的點,一般是方法的呼叫 (3)Advice(
Spring AOP切面基礎 實現請求引數的驗證
1.應用的場景 對部分函式的呼叫進行日誌記錄,用於觀察特定問題在執行過程中的函式呼叫情況監控部分重要函式,若丟擲指定的異常,需要以簡訊或郵件方式通知相關人員監控部分重要函式的執行時間,更靈活植入和取消介面報文的引數驗證基本的Demo程式碼 切面類 package co
Spring AOP使用Aspectj基於xml方式,初始化Bean引數
場景: 大多數實體繼承了一個BaseBean,這裡所做的就是使用Spring的Aop功能實現,攔截到的方法,對其引數做一些處理。 spring-xxx.xml的核心配置: <aop:aspectj-autoproxy proxy-ta
Spring原始碼系列(三)--spring-aop的基礎元件、架構和使用
# 簡介 前面已經講完 spring-bean( 詳見[Spring](https://www.cnblogs.com/ZhangZiSheng001/category/1776792.html) ),這篇部落格開始攻克 Spring 的另一個重要模組--spring-aop。 spring-aop 可以
[Spring框架]Spring AOP基礎入門總結二:Spring基於AspectJ的AOP的開發.
work 復制代碼 配置文件 exec dao tool ont pda nbsp 前言: 在上一篇中: [Spring框架]Spring AOP基礎入門總結一. 中 我們已經知道了一個Spring AOP程序是如何開發的, 在這裏呢我們將基於AspectJ來進行AOP
基於代理的spring aop中多種通知實現
不需要引入額外的jar包,只需引入需要模組的spring內部jar包即可. 介面結構 標註的都是標記介面,其中大多數有明確約定的介面實現,只有異常通知介面沒有,但有其預設約定. Advice spring aop通知的頂層標記介面
Spring AOP 基於 XML ---- 宣告通知
知識點如下 例項如下 介面類 package cn.com.day02; public interface Calculator { /* * author:命運的信徒 date:2018/12/21 arm:aop基礎 * 這裡是四個方法;返
JavaEE框架——Spring入門基礎(控制反轉Ioc和切面技術Aop)
一.簡介: Spring在英語中含義是春天,對於JavaEE開發者來說,Spring框架出現確實帶來了一股全新的春天的氣息。早在2002年,Rod Johson在其編著的《Expert one to one J2EE design anddevelopment》書中,對J
【Spring Boot入門】AOP基礎及Advice的執行順序
本文主要分為兩個部分,首先介紹AOP的基礎,包括為什麼要使用AOP以及AOP中的基本概念,然後講解AOP中各類Advice的執行順序並給出簡單示例。 一、AOP基礎 1、為什麼要使用AOP AOP(Aspect Oriented Programmi
使用Spring的註解方式實現AOP入門
單元測試 comment cast override src ioc ans 文件 返回 首先在Eclipse中新建一個普通的Java Project,名稱為springAOP。為了使用Spring的註解方式進行面向切面編程,需要在springAOP項目中加入與AOP相關的
Spring Boot入門第二天:一個基於Spring Boot的Web應用,使用了Spring Data JPA和Freemarker。
per pan let mysq 應用 posit ble host thead 今天打算從數據庫中取數據,並展示到視圖中。不多說,先上圖: 第一步:添加依賴。打開pom.xml文件,添加必要的依賴,完整代碼如下: <?xml version="1.0" enco
spring-AOP之通知和顧問
多個 targe ges 配置 context color ive 後置 功能 通知和顧問都是切面的實現形式,其中通知可以完成對目標對象方法簡單的織入功能。 而顧問包裝了通知,可以讓我們對通知實現更加精細化的管理,讓我們可以指定具體的切入點。 通知分為前置通知,環繞通知及後
Java動態代理學習【Spring AOP基礎之一】
tor -1 我們 null exception 文件 cat static 一個 Spring AOP使用的其中一個底層技術就是Java的動態代理技術。Java的動態代理技術主要圍繞兩個類進行的 java.lang.reflect.InvocationHan
CgLib動態代理學習【Spring AOP基礎之一】
div 目前 .get 不知道 ctu get() 內容 想要 外部依賴 如果不了解JDK中proxy動態代理機制的可以先查看上篇文章的內容:Java動態代理學習【Spring AOP基礎之一】 由於Java動態代理Proxy.newProxyInstance()的
Spring AOP的用法
val around 技術 pac factor 日常 三種方式 viso 可復用 AOP在事務處理、日誌、安全等方面用的很多,在日常軟件定制開發中,用好AOP可以進一步解耦,增強代碼的可復用性。平時用的最多的還是Spring AOP動態代理,其用法如下: 第一種實現
Spring AOP高級——源碼實現(2)Spring AOP中通知器(Advisor)與切面(Aspect)
color oaf 小麻煩 ntc tro sta ins pack package 本文例子完整源碼地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/Spring%20AO