自定义注解 + Spring AOP

元注解

元注解的作用就是负责注解其他注解,Java 5.0定义了 4个meta-annotation 类型,用来提供对爱他的 annotation 类型做说明。

java.lang.annotation

  • @Target

  • @Retention

  • @Document

  • @Inhrited

@Target

修饰的对象范围:packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。

作用:用于描述注解的使用范围。

ElementType 取值:

1. CONSTRUCTOR:用于描述构造器

2. FIELD:用于描述域

3. LOCAL_VARIABLE:用于描述局部变量

4. METHOD:用于描述方法

5. PACKAGE:用于描述包

6. PARAMETER:用于描述参数

7. TYPE:用于描述类、接口(包括注解类型) 或enum声明

例如,Name 可以注解类的成员变量:

@Target(ElementType.FIELD)@Documented
public @interface Name {    String value() default "";
}

Person 可以注解类、接口(包括注解类型)、或者enum声明:

@Target(ElementType.TYPE)
public @interface Person {
    String value() default "";
}

@Retention

定义了该 Annotation 被保留的时间长短:某些 Annotation 仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在 class 被装载时将被读取(请注意并不影响 class 的执行,因为 Annotation 与 class 在使用上是被分离的)。使用这个 meta-Annotation 可以对  Annotation 的“生命周期”限制。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

RetentionPoicy取值:

  • SOURCE:在源文件中有效(即源文件保留)

  • CLASS:在class文件中有效(即class保留)

  • RUNTIME:在运行时有效(即运行时保留)

例如:Name 注解的 RetentionPolicy 的值为 RUNTIME,这样注解处理器可以通过反射,获取到该注解的属性,从而做一些运行时的逻辑处理。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
    String value() default "";
}

@Document

用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共API,因此可以被例如 javadoc 此类的工具文档化。Documented 是一个标记注解,没有成员。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Name {
    String value() default "";
}

@Inhrited

是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited 修饰的 annotation类型 被用于一个 class,则这个 annotation 将被用于该class的子类。

@Inherited annotation类型 是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation

当 @Inherited annotation类型 标注的 annotation 的 Retention 是 RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect 去查询一个 @Inherited annotation类型 的 annotation 时,反射代码检查将展开工作:检查class和其父类,直到发现指定的 annotation 类型被发现,或者到达类继承结构的顶层。

自定义注解

使用 @interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface 用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是 基本类型、Class、String、enum)。可以通过 default 来声明参数的默认值。

自定义注解格式:

public @interface 注解名{注解体}

  • 所有基本数据类型(int,float,boolean,byte,double,char,long,short)

  • String类型

  • Class类型

  • enum类型

  • Annotation类型

  • 以上所有类型的数组

Annotation类型里面的参数该怎么设定:

第一,只能用 public 或 默认(default) 这两个访问权修饰.例如,String value();这里把方法设为 defaul 默认类型;

第二,参数成员只能用基本类型 byte,short,char,int,long,float,double,boolean 八种基本数据类型 和 String,Enum,Class,annotations 等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;

第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.

1.创建一个自定义注解

2.定义切面类

3.接上添加注解

打印日志 

切点访问顺序

异常情况 (最终通知报错之后依然可以执行)

4.代码粘贴如下

自定义注解

package com.jlink.common.annotation;

import java.lang.annotation.*;

/**
 * @author jyg
 * @create 2024-07-16-11:25
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {

    String text() default "";
}

定义切面类

package com.jlink.framework.aspectj;

import com.jlink.common.annotation.MyAnnotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author jyg
 * @create 2024-07-16-13:19
 */
@Aspect
@Component
public class MyAnnotationAspect {

  private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);

  // 定义通用的表达式切点
  @Pointcut("@annotation(com.jlink.common.annotation.MyAnnotation)")
  public void pointcut() {
    // 方法只是一个标记,方法名随意,方法体也不需要写任何代码
  }

  /**
   * 前置通知
   *
   * @param point
   */
  @Before("pointcut()") // 切面表达式
  public void doBefore(JoinPoint point) {
    // 这里可以获取到连接点的信息,如方法名、参数等
    MethodSignature signature = (MethodSignature) point.getSignature();
    // 但是这种方法只能通过反射机制获取注解
    MyAnnotation myAnnotation = signature.getMethod().getAnnotation(MyAnnotation.class);

    System.out.println("接口访问前,入参的值:" + myAnnotation.text());
  }

  /**
   * 后置通知
   *
   * @param joinPoint 切点
   */
  //  pointcut 属性定义了切点表达式,用于指定哪些方法应该被拦截。
  //  returning 属性(可选)用于指定一个参数名,该参数将绑定到目标方法的返回值上。
  @AfterReturning(pointcut = "@annotation(myAnnotation)", returning = "result")
  public void doAfterReturning(JoinPoint joinPoint, MyAnnotation myAnnotation, Object result) {

    // 注解的值
    String text = myAnnotation.text();
    System.out.println("接口访问完了" + text);

    // joinPoint 参数包含了被拦截方法的详细信息
    // result 参数包含了被拦截方法的返回值
    System.out.println(
        "Method "
            + joinPoint.getSignature().getName()
            + " executed successfully. Returned: "
            + result);
  }

  /**
   * 环绕通知
   *
   * @param proceedingJoinPoint proceedingJoinPoint
   * @param myAnnotation myAnnotation
   * @throws Throwable
   */
  @Around("@annotation(myAnnotation)")
  public void doAround(ProceedingJoinPoint proceedingJoinPoint, MyAnnotation myAnnotation)
      throws Throwable {

    // 方法需要接受一个ProceedingJoinPoint类型的参数
    String before = proceedingJoinPoint.getSignature().getName();
    System.out.println("环绕通知前" + before);

    // 调用目标方法
    Object proceed = proceedingJoinPoint.proceed();

    // 在这里可以添加后置逻辑,比如打印方法调用后的信息或处理返回值
    String after = proceedingJoinPoint.getSignature().getName();
    System.out.println("环绕通知后" + after);
  }

  /**
   * 异常通知
   *
   * @param joinPoint 连接点
   * @param ex 异常
   * @param myAnnotation 注解
   */
  @AfterThrowing(pointcut = "@annotation(myAnnotation)", throwing = "ex")
  public void logAfterThrowing(JoinPoint joinPoint, Exception ex, MyAnnotation myAnnotation) {
    // 在这里记录日志、发送警报或其他处理异常的代码
    System.out.println(
        "异常就走这里------Exception in method " + joinPoint.getSignature().getName() + ": " + ex.getMessage());
  }

  /**
   * 最终通知
   *
   * @param myAnnotation
   */
  @After("@annotation(myAnnotation)")
  public void afterAdvice(MyAnnotation myAnnotation) {
    // 这里的代码会在 MyAnnotation 方法执行后执行
    // 无论是正常执行完成还是抛出异常
    System.out.println("最终通知------Method executed.");
  }
}

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值