AOP:在程式運作期間,動态的将某段代碼切入到指定方法運作時的指定時機運作,其實就是動态代理。
Spring提供了對AOP很好的支援,使用時需要導入spring-aspects包。
業務邏輯類:要求在業務方法運作時列印日志
public class MathCalculator {
public int div(int i,int j){
return i/j;
}
}
切面類:類上需要注解@Aspect,切面類中的方法需要動态感覺業務方法的執行情況
@Aspect
public class AopAspect {
@Before("public int com.bdm.aop.MathCalculator.div(int, int)")
public void logStart() {
System.out.println("計算開始");
}
@After("public int com.bdm.aop.MathCalculator.*(int, int)")
public void logEnd() {
System.out.println("計算結束");
}
@AfterReturning("public int com.bdm.aop.MathCalculator.*(..)")
public void logReturn() {
System.out.println("計算結果");
}
@AfterThrowing("public int com.bdm.aop.MathCalculator.div(..)")
public void logException() {
System.out.println("計算異常");
}
}
切面類中的方法也稱為通知方法:
前置通知(@Before):在目标方法運作之前運作
後置通知(@After):在目标方法運作之後運作,即使出現異常也會運作
傳回通知(@AfterReturning):在目标方法正常傳回之後運作
異常通知(@AfterThrowing):在目标方法運作出現異常之後運作
環繞通知(@Around):動态代理,手動推進目标方法的運作
在使用這些注解的時候必須配合切入點表達式,來指明在哪些方法執行之前、之後、傳回時、異常時執行,在寫切入點表達式時可以使用通配符*表示所有方法或者類,入參可以使用..表示所有參數類型
如果切入點表達式一樣的話,則可以使用一個方法抽取切入點表達式,注意該方法的名稱可以任意,而且方法體内無需寫任何内容,隻需要使用@PointCut注解并将切入點表達式标明即可:
@Aspect public class AopAspect {
@Pointcut("execution(public int com.bdm.aop.MathCalculator.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void logStart() {
System.out.println("計算開始");
}
@After("com.bdm.aop.AopAspect.pointCut()")
public void logEnd() {
System.out.println("計算結束");
}
@AfterReturning("pointCut()")
public void logReturn() {
System.out.println("計算結果");
}
@AfterThrowing("com.bdm.aop.AopAspect.pointCut()")
public void logException() {
System.out.println("計算異常");
}
}
本類中使用時可直接寫方法名,其他類也想使用此切入點表達式時則需要使用該方法的全類名
容器會将标注有@Aspect注解的類認為是切面類
使用Spring的切面需要開啟Spring的切面自動代理,隻需要在配置類中加注解@EnableAspectJAutoProxy,Spring中有很多@EnableXxx注解,用來開啟一些功能
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
@Bean
public MathCalculator cal(){
return new MathCalculator();
}
@Bean
public AopAspect aspect(){
return new AopAspect();
}
}
配置bean怎麼區分哪個bean是切面類呢,它會看哪個類上有@Aspect注解,另外切面方法的執行僅對Spring容器中的bean起作用,對于我們自己new出來的對象是不起作用的,原因也很簡單,我們自己建立的bean并沒有被spring管理,也就沒有為其設定切面方法等。
通過JoinPoint對象擷取調用目标方法時的資訊,比如方法名、參數等,使用returning指定用通知方法的哪個入參接收傳回值,使用throwing指定用哪個入參接收異常,另外如果使用JoinPoint,則必須将其放在切面方法入參的第一個位置,否則會報錯:
@Aspect
public class AopAspect {
@Pointcut("execution(public int com.bdm.aop.MathCalculator.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void logStart(JoinPoint point) {
String name = point.getSignature().getName();
Object[] args = point.getArgs();
System.out.println(name + "方法計算開始,入參:" + Arrays.asList(args));
}
@After("com.bdm.aop.AopAspect.pointCut()")
public void logEnd(JoinPoint point) {
String name = point.getSignature().getName();
System.out.println(name + "方法計算結束");
}
@AfterReturning(value="pointCut()",returning="obj")
public void logReturn(JoinPoint point,Object obj) {
String name = point.getSignature().getName();
System.out.println(name +"方法的計算結果:" + obj.toString());
}
@AfterThrowing(value="com.bdm.aop.AopAspect.pointCut()",throwing="exception")
public void logException(Exception exception) {
System.out.println("計算異常:"+ exception.getMessage());
}
}
總結:
1、将切面類(@Aspect标注的類)納入到配置類中,業務類對象也必須由Spring容器建立
2、切面方法加上通知注解(@Before、@After、@AfterReturning、@AfterThrowing、@Round),并寫上切入點表達式,告訴切面方法在執行哪些方法前、後等時點執行這些切面方法
3、啟用基于注解的AOP模式:配置類上加注解@EnableAspectJAutoProxy