1.使用AOP前的準備工作
1.1 引入相關的jar包
1.2 在spring配置文件的beans添加aop規範
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"
2. AOP增強使用方式
2.0 執行順序
- Before前置增強→原方法執行→[異常增強]→After最終增強→AfterReturning後置返回增強(有異常不會執行AfterReturning)
2.1 增強類型
- 2.1.1前置增強:在被增強的方法執行前攔截
package com.aop;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.aop.MethodBeforeAdvice;
//實現MethodBeforeAdvice接口表示該類爲前置增強類
public class BeforeAdvice implements MethodBeforeAdvice {
/*
*參數method表示被增強的方法的方法對象
*參數args表示被增強的方法的參數數組
*參數target表示被增強的方法所屬的類的對象
*/
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("調用"+target+"的"+method.getName()+"方法,方法入參:"+Arrays.toString(args));
}
}
- 2.1.2 後置增強 :在被增強的方法執行後攔截
- 最終增強與後置增強要區分好
package com.aop;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.aop.AfterReturningAdvice;
//實現AfterReturningAdvice接口表示該類爲後置增強類
public class AfterAdvice implements AfterReturningAdvice {
/*
*參數result表示被增強的方法的返回值
*參數method表示被增強的方法的方法對象
*參數args表示被增強的方法的參數數組
*參數target表示被增強的方法所屬的類的對象
*/
public void afterReturning(Object result, Method method, Object[] args,
Object target) throws Throwable {
System.out.println("調用"+target+"的"+method.getName()+"方法,方法入參:"+Arrays.toString(args)+",方法返回值是"+result);
}
}
- 2.1.3 異常拋出增強:在被增強的方法拋出異常後攔截
package com.aop;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
//實現ThrowsAdvice接口表示該類爲異常拋出增強類
public class ExceptionAdvice implements ThrowsAdvice{
/*
*參數method表示被增強的方法的方法對象
*參數args表示被增強的方法的參數數組
*參數target表示被增強的方法所屬的類的對象
*參數e表示異常,類型可以是任意的運行時異常
*/
/*
* 1、實現ThrowsAdvice接口跟其他增強接口不同,它不會提供必須實現的方法
* 2、方法命名規則: void afterThrowing([Method method, Object[] args, Object target,] Throwable e)
* 中括號包含的參數要麼全要,要麼全都不要,異常參數是必須的,方法名必須是afterThrowing
* 3、異常拋出增強類不能catch異常,執行完afterThrowing方法後程序依然會終止
*
*/
public void afterThrowing(Method method, Object[] args, Object target,RuntimeException e) {
System.out.println("出現錯誤:"+e.getMessage());
}
}
- 2.1.4 環繞增強:在被增強的方法執行前和執行後都進行攔截
package com.aop;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//實現MethodInterceptor接口表示該類爲環繞增強類
public class AroundAdvice implements MethodInterceptor{
//參數ai包含了被代理的對象,方法,參數等
public Object invoke(MethodInvocation ai) throws Throwable {
Object target=ai.getThis(); //獲取被代理的對象
Method method=ai.getMethod(); //獲取被代理的方法
Object[] args=ai.getArguments(); //獲取方法參數
System.out.println("調用"+target+"的"+method.getName()+"方法,方法入參:"+Arrays.toString(args));
try {
//proceed()方法調用目標對象的相應方法
Object result=ai.proceed();
System.out.println("調用"+target+"的"+method.getName()+"方法,方法入參:"+Arrays.toString(args)+",方法返回值是"+result);
return result;
} catch (Exception e) {
//拋出異常
System.out.println(method+ "方法出現異常:"+e);
throw e;
}
}
}
- 2.1.5 最終增強:即使拋出異常也會進行攔截,跟finally差不多
public class FinalAdvice implements AfterAdvice {}
2.2 切入點表達式
-
格式
訪問修飾符? 方法返回值類型 [類的全局路徑.]? 方法名(參數列表)
-
分析
- 主要分成4部分:訪問修飾符(可有可無)、方法返回值類型、方法、參數列表
- 方法是由
類的全局路徑.方法名
組成,類的全局路徑可省略,表示任意包下的所有類的指定方法 - 訪問修飾符丶方法返回值類型丶方法名使用
*
表示任意的返回值類型丶任意訪問修飾符丶任意方法 - 方法使用
包名.*.*
表示指定包名下所有類的所有方法,第一個.*
是所有類,第二個.*
是所有方法 - 方法使用
包名..*.*
表示指定包名及其子包下的所有類的所有方法,第一個.
表示其所有子包,第二個.*
是所有類,第三個.*
次表示所有方法 - 方法的
字符.*
表示指定包的指定類的所有方法,字符*
表示以指定字符開頭的方法 - 參數使用
..
表示任意類型任意個數任意順序的參數列表
-
例子
//公有的任意返回值類型的帶一個User類型參數的save方法
* public * save(com.entity.User)
//所有的公有方法
* public * *(..)
//表示com.biz.impl包下的所有類的所有方法
* * com.biz.impl.*.*(..)
//表示com.biz.impl包及其子包下的所有類的所有方法
* * com.biz.impl..*.*(..)
//表示所有以Impl開頭的方法
* * Impl*(..)
2.3 spring配置文件添加AOP配置
<!-- 配置AOP -->
<aop:config>
<!-- aop:pointcut標籤配置切入點,id屬性爲切入點的名字,expression屬性爲切入點指示符 -->
<!-- 這裏切入點爲 com.biz.impl包及其子包下的所有類的所有方法-->
<aop:pointcut id="pointcut" expression="execution(* com.biz.impl..*.*(..))" />
<!-- aop:advisor標籤配置增強處理器,增加類實現了接口纔可以用這個標籤,advice-ref屬性爲增強處理器的實例id,pointcut-ref屬性爲切入點id-->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pointcut" />
<aop:advisor advice-ref="afterAdvice" pointcut-ref="pointcut" />
<aop:advisor advice-ref="exceptionAdvice" pointcut-ref="pointcut" />
<aop:advisor advice-ref="aroundAdvice" pointcut-ref="pointcut" />
<!-- aop:aspect標籤用於沒有實現接口的普通增強類 -->
<!-- ref爲普通增強類的實例id -->
<aop:aspect ref="myAspect">
<!-- aop:before表示前置增強 -->
<!-- method表示普通增強類中哪個方法作爲前置增強方法 -->
<!-- pointcut-ref表示切入點id -->
<aop:before method="before" pointcut-ref="pointcut"/>
<!-- 這裏還可以配置其他類型的增強 -->
</aop:aspect>
</aop:config>
3. AOP註解
3.1 增強類
package com.advice;
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;
//@Aspect標誌一個類是增強類
@Aspect
public class LogAdvice {
//定義切入點方法
@Pointcut(value="execution(* com.biz.impl..*.del(..))")
private void pointcut1(){}
//Before(pointcut)表示前置增強
//pointcut的值可以引用切入點方法
@Before("pointcut1()")
public void before(JoinPoint jp){
//JoinPoint可以獲取被增強的方法信息
System.out.println("【前置增強】開始執行方法"+jp.getSignature());
}
//AfterReturning(pointcut,returning)表示後置增強,出現異常不會進入本方法
//returning="returnValue"表示把被增強的返回值注入到參數returnValue中
@AfterReturning(pointcut="pointcut1()",returning="result")
public void afterReturning(JoinPoint jp,Object result){
System.out.println("【後置增強】"+jp.getSignature().getName()+"方法執行完畢,返回值是"+result);
}
//AfterThrowing(pointcut,throwing)表示異常增強,出現異常時會進入本方法,但是不能捕捉異常
@AfterThrowing(pointcut="pointcut1()",throwing="e")
public void afterThrowing(JoinPoint jp,RuntimeException e){
System.out.println(jp.getSignature().getName()+"方法發生異常:"+e.getMessage());
}
//After(pointcut)表示最終增強,相當於finally塊,即使出現異常也會進入本方法
@After("pointcut1()")
public void after(JoinPoint jp){
System.out.println("【最終增強】"+jp.getSignature().getName());
}
//Around(pointcut)表示環繞增強,可以通過代碼編寫實現上面幾種增強的功能
@Around("execution(* com.biz.impl..*.save(..))")
public void around(ProceedingJoinPoint pjp){
System.out.println("======================");
String methodName=pjp.getSignature().getName();
Object result=null;
try {
System.out.println("【環繞增強-前置】開始進入方法"+methodName);
result=pjp.proceed();
} catch (Throwable e) {
System.out.println("【環繞增強-異常】"+methodName+"方法出錯了"+e.getMessage());
}finally{
System.out.println("【環繞增強-最終】"+methodName+",最終");
}
System.out.println("【環繞增強-後置】方法執行完畢"+methodName+",返回值是"+result);
}
}
3.2 在spring配置文件中開啓aspectj自動代理
<!-- 此處省略導入aop命名空間代碼 -->
<!-- 1.開啓aspectj自動代理,啓用對註解的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 2.創建增強類的實例 -->
<bean id="loggerAdvice" class="com.advice.LoggerAdvice"></bean>