日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

AOP切面編程,以及自定義注解實(shí)現(xiàn)切面

作者:Bunny0212 更新時(shí)間: 2024-03-14 編程語言

AOP切面編程

  1. 通知類型
  2. 表達(dá)式
  3. 重用表達(dá)式
  4. 切面優(yōu)先級
  5. 使用注解開發(fā),加上注解實(shí)現(xiàn)某些功能

簡介

  • 動(dòng)態(tài)代理分為JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理
  • 當(dāng)目標(biāo)類有接口的情況使用JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,沒有接口時(shí)只能使用cglib動(dòng)態(tài)代理
  • JDK動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類會(huì)在com.sun.proxy包下,類名為$proxy1,和目標(biāo)類實(shí)現(xiàn)相同的接口
  • cglib動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類會(huì)和目標(biāo)在在相同的包下,會(huì)繼承目標(biāo)類
  • 動(dòng)態(tài)代理(InvocationHandler):JDK原生的實(shí)現(xiàn)方式,需要被代理的目標(biāo)類必須實(shí)現(xiàn)接口。因?yàn)檫@個(gè)技術(shù)要求代理對象和目標(biāo)對象實(shí)現(xiàn)同樣的接口(兄弟兩個(gè)拜把子模式)。
  • cglib:通過繼承被代理的目標(biāo)類(認(rèn)干爹模式)實(shí)現(xiàn)代理,所以不需要目標(biāo)類實(shí)現(xiàn)接口。
  • AspectJ:是AOP思想的一種實(shí)現(xiàn)。本質(zhì)上是靜態(tài)代理,將代理邏輯“織入”被代理的目標(biāo)類編譯得到的字節(jié)碼文件,所以最終效果是動(dòng)態(tài)的。weaver就是織入器。Spring只是借用了AspectJ中的注解。

依賴

<!--spring context依賴-->
<!--當(dāng)你引入Spring Context依賴之后,表示將Spring的基礎(chǔ)依賴引入了-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.2</version>
</dependency>

<!--spring aop依賴-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.2</version>
</dependency>
<!--spring aspects依賴-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.2</version>
</dependency>

注解說明

  • @Aspect表示這個(gè)類是一個(gè)切面類
  • @Component注解保證這個(gè)切面類能夠放入IOC容器

簡單使用

幾種通知類型

  • 前置通知@Before()
  • 環(huán)繞通知@Around()
  • 返回通知@AfterReturning()
  • 異常通知@AfterThrowing()
  • 后置通知@After()

表達(dá)式介紹

在這里插入圖片描述

切入點(diǎn)表達(dá)式

在方法中可以傳入?yún)?shù)public void afterMethod(JoinPoint joinPoint),類型為JoinPoint

  • 獲取連接點(diǎn)的簽名信息

    • String methodName = joinPoint.getSignature().getName()
      
  • 獲取目標(biāo)方法的返回值

    • 其中表達(dá)式中returning后面參數(shù)result要與public void afterReturningMethod(JoinPoint joinPoint, Object result)傳入?yún)?shù)名稱一樣,類型可以不一樣但是名稱要一樣。

    • @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
      public void afterReturningMethod(JoinPoint joinPoint, Object result){
          String methodName = joinPoint.getSignature().getName();
          System.out.println("Logger-->返回通知,方法名:"+methodName+",結(jié)果:"+result);
      }
      
  • 獲取目標(biāo)方法的異常

    • 在表達(dá)式中加入throwing = "ex",和上面一樣,傳入?yún)?shù)名稱要一直,也要是ex,如:Throwable ex

    • @AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
      public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
          String methodName = joinPoint.getSignature().getName();
          System.out.println("Logger-->異常通知,方法名:"+methodName+",異常:"+ex);
      }
      
切入點(diǎn)表達(dá)式使用

前置通知

@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
public class LogAspect {
    // 設(shè)置切入點(diǎn)和通知類型
    @Before(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->前置通知" + "參數(shù):" + args[0] + "," + args[1]);
    }
}

后置通知

@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
public class LogAspect {
    @After(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();// 方法名

        System.out.println("Logger-->后置通知,方法名:" + name);
    }
}

返回通知

@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
public class LogAspect {
    @AfterReturning(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名:" + methodName + ",結(jié)果:" + result);
    }
}

異常通知

@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
public class LogAspect {
    @AfterThrowing(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->異常通知,方法名:" + methodName + ",異常:" + ex);
    }
}

環(huán)繞通知

@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
public class LogAspect {
    @Around(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("環(huán)繞通知-->目標(biāo)對象方法執(zhí)行之前");
            // 目標(biāo)對象(連接點(diǎn))方法的執(zhí)行
            result = joinPoint.proceed();
            System.out.println("環(huán)繞通知-->目標(biāo)對象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("環(huán)繞通知-->目標(biāo)對象方法出現(xiàn)異常時(shí)");
        } finally {
            System.out.println("環(huán)繞通知-->目標(biāo)對象方法執(zhí)行完畢");
        }
        return result;
    }
}
重用切入點(diǎn)表達(dá)式

申明表達(dá)式

@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}

在方法中使用

@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}

在不同切面中使用

@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}

在這里插入圖片描述

切面的優(yōu)先級

相同目標(biāo)方法上同時(shí)存在多個(gè)切面時(shí),切面的優(yōu)先級控制切面的內(nèi)外嵌套順序。

  • 優(yōu)先級高的切面:外面
  • 優(yōu)先級低的切面:里面

使用@Order注解可以控制切面的優(yōu)先級:

  • @Order(較小的數(shù)):優(yōu)先級高
  • @Order(較大的數(shù)):優(yōu)先級低

將@Order注解放在切面類上而不是方法上!!!

  • 優(yōu)先級低的
@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
@Order(4)
public class LogAspect {
    @After(value = "pointcut()")
    public void afterMethod2(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();// 方法名

        System.out.println("Logger-->后置通知 111111,方法名:" + name);
    }
}
  • 優(yōu)先級高的
@Aspect// 表示這個(gè)類是一個(gè)切面類
@Component// 注解保證這個(gè)切面類能夠放入IOC容器
@Order(1)
public class NewLogAspect {
    @After(value = "com.example.aop.annoaop.LogAspect.pointcut()")
    public void afterMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();// 方法名

        System.out.println("Logger-->后置通知 444444,方法名:" + name);
    }
}
效果

原先交換順序后

在這里插入圖片描述

沒有交換順序前

在這里插入圖片描述

注解使用

使用AOP在方法或者接口上寫上某些注解,完成特定方法。

實(shí)現(xiàn)思路

  1. 創(chuàng)建@interface
  2. 和上面一樣要寫Aspect,并且要被spring管理
  3. 在方法或者接口上加上@interface注解

實(shí)現(xiàn)示例

  • 創(chuàng)建@interface 并命名為BunnyLog
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
// 加上這個(gè)注解打印當(dāng)前時(shí)間
public @interface BunnyLog {
    
}
  • 使用AOP切面,需要注意的是:

    • 如果只是單純的加上注解,不考慮指定類或者類中方法,完成某些功能,要修改下面切入點(diǎn)

      • @Pointcut(value = "@annotation(com.example.aop.annoaop.BunnyLog)")
        public void bunnyPointcut() {
        }
        
    • 如果想指定某些類或者某些方法下的

      • @Pointcut("execution(* com.example.aop.annoaop.*.*(..)) && @annotation(com.example.aop.annoaop.BunnyLog)")
        public void bunnyPointcut() {
        }
        
  • 創(chuàng)建切面

@Aspect
@Component
public class BunnyAspect {
    /**
     * 切入點(diǎn),并且加上了 @BunnyLog注解
     */
    @Pointcut(value = "execution(* com.example.aop.annoaop.*.*(..)) || @annotation(com.example.aop.annoaop.BunnyLog)")
    public void bunnyPointcut() {
    }

    @Before("bunnyPointcut()")
    public void before(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println("------AOP前置通知生效,方法名稱------>" + name);

        LocalDateTime localDateTime = LocalDateTime.now();
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyy年MM月dd日 HH:mm:ss");
        String format = localDateTime.format(timeFormatter);
        System.out.println("BunnyAspect,現(xiàn)在時(shí)間====>" + format);
    }
}
  • 普通方法,需要在類上加上@Component被spring管理,之后再方法中加上注解@BunnyLog
@Component
public class BunnyTestImpl {
    @BunnyLog
    public void method() {
        LocalDateTime localDateTime = LocalDateTime.now();
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyy年MM月dd日 HH:mm:ss");
        String format = localDateTime.format(timeFormatter);
        System.out.println("測試方法,現(xiàn)在時(shí)間====>" + format);
    }
}
  • 創(chuàng)建一個(gè)測試類
public class TestBefore {
    @Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        BunnyTestImpl bean = context.getBean(BunnyTestImpl.class);
        bean.method();
    }
}

執(zhí)行結(jié)果:

r);
System.out.println(“測試方法,現(xiàn)在時(shí)間====>” + format);
}
}


- 創(chuàng)建一個(gè)測試類

```java
public class TestBefore {
    @Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        BunnyTestImpl bean = context.getBean(BunnyTestImpl.class);
        bean.method();
    }
}

執(zhí)行結(jié)果:

在這里插入圖片描述

原文鏈接:https://blog.csdn.net/weixin_46533577/article/details/136605066

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新