如何获取注解的参数值

文章介绍了Java注解在编译期和运行期的处理方式。在编译期,可以通过自定义AnnotationProcessor来处理。在运行期,特别是在Spring框架中,可以使用@Aspect进行切面编程来处理注解,例如文中展示了如何在方法级别处理自定义注解并获取其参数值。@Around注解配合execution表达式提供了灵活的功能,能精确匹配到特定的类和方法。

网上关于这一点的文章似乎不很多,随手记一笔。

注解的处理

注解可以用在2个阶段:编译期和运行期。

编译期注解可用javac的Annotation Processor 来处理,我们需要定义自己的Annotation Processor 供javac使用,这里面有一定的规范,可参考相关文章解决。

运行期注解在spring下可用@Aspect切面编程来处理,比如我们的注解如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String lockName();

    int leaseTime();
}

处理该注解的方法如下:

@Component
@Aspect
@Slf4j
public class CustomAspect {

    ...

    @Around("@annotation(annoInst)")
    public Object doAround(ProceedingJoinPoint pjp, MyAnnotation annoInst) throws Throwable {
        // 前置处理
        ...
            
        try {
            return pjp.proceed();
        } finally {
            // 后置处理
            ...
        }
    }
}

我们先用@Component+@Aspect定义一个处理注解的spring bean。这里我们要获取注解的参数值,所以doAround函数要加上如下注解:

@Around("@annotation(annoInst)")

表明我们的环绕(around)操作要处理的是一个MyAnnotation类型注解的实例annoInst,annoInst必须声明为doAround函数的一个参数

如果我们并不需要处理注解参数,可以这样写:

@Component
@Aspect
@Slf4j
public class CustomAspect {

    ...

    @Around("@annotation(MyAnnotation)")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // 前置处理
        ...
            
        try {
            return pjp.proceed();
        } finally {
            // 后置处理
            ...
        }
    }
}

注意:这里我们仅仅在@annotation里声明了注解的类型,且doAround函数参数里也不带MyAnnotation注解实例。

顺带说一下,@Around除了@annotation,还能支持execution表达式,后者能力非常灵活,可以指定到特定类的特定函数,网上有很多介绍文章,这里就不赘述了。

<think>我们正在讨论如何在Java中定义注解并限制注解参数的取值范围。根据引用内容,特别是引用[2]和引用[4],我们可以使用@IntDef(对于整型)或@StringDef(对于字符串)等元注解来限定参数值的范围。此外,引用[1]提到如果注解有配置参数,则必须使用指定参数值,格式为“参数名=参数值”,并且如果只有一个参数且名称为value,可以省略“value=”。 步骤: 1. 定义注解:使用@interface定义一个新的注解类型。 2. 使用元注解:使用@Retention指定注解的保留策略(通常为SOURCE,因为这类注解主要用于编译时检查),使用@Target指定注解可以应用的位置(如参数、字段等)。 3. 限定取值范围:使用@IntDef(或其他类似注解)指定允许的取值,该元注解需要一个value参数,其值为允许的常量数组。 4. 定义常量:定义一组常量,这些常量将作为注解参数允许的值。 注意:在Android开发中,为了性能考虑(避免枚举带来的内存开销),推荐使用注解来限定整型或字符串参数。 示例:假设我们要定义一个注解@LogLevel,限定其值只能是几个特定的整数(代表日志级别)。 首先,定义常量类(或接口)来存放允许的值: ```java public class Level { public static final int LEVEL_V = 0; public static final int LEVEL_D = 1; public static final int LEVEL_I = 2; public static final int LEVEL_W = 3; public static final int LEVEL_E = 4; } ``` 然后,定义注解@LogLevel: ```java import static java.lang.annotation.RetentionPolicy.SOURCE; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; // 可以根据需要添加其他目标类型,比如方法、局部变量等 @Retention(SOURCE) // 注解只在源码级别保留,不编译到class文件中,也不在运行时保留 @Target({PARAMETER, FIELD}) // 可以用于参数和字段 @IntDef(value = {Level.LEVEL_V, Level.LEVEL_D, Level.LEVEL_I, Level.LEVEL_W, Level.LEVEL_E}) public @interface LogLevel { } ``` 这样,我们就可以在方法参数或字段上使用@LogLevel注解,并且只能使用Level类中定义的常量值。 使用示例: ```java public void setLogLevel(@LogLevel int level) { // ... } ``` 此时,如果调用setLogLevel方法时传入的值不是Level中定义的常量(比如传入5),IDE(如Android Studio)会给出警告或错误(取决于IDE配置)。注意,这只是在编译时进行静态检查,并不会在运行时强制检查(因为注解在运行时已经不存在了)。如果需要在运行时检查,需要自行编写验证逻辑。 另外,引用[1]中提到,如果注解只有一个参数且名称为value,可以省略“value=”。但在定义@IntDef时,我们只有一个value参数,所以我们可以直接写常量数组,不需要写“value=”。 对于字符串的限定,可以使用@StringDef,用法类似。 总结:通过使用@IntDef、@StringDef等元注解,我们可以为注解参数(或方法参数、字段)限定取值范围,从而在编译时提供类型安全的检查。 相关问题: 1. 如何为自定义注解定义多个参数? 2. 如何在运行时获取注解信息并进行验证? 3. 自定义注解的保留策略(RetentionPolicy)有哪些,分别适用于什么场景? 4. 除了参数和字段,自定义注解还可以应用在哪些目标上?</think>### Java注解参数值限定方法 在Java中定义注解并限定参数取值范围,主要通过元注解和常量定义实现。核心步骤如下: #### 1. **定义常量集合** ```java public class LogLevel { public static final int VERBOSE = 0; public static final int DEBUG = 1; public static final int INFO = 2; public static final int WARN = 3; public static final int ERROR = 4; } ``` #### 2. **创建自定义注解** ```java import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import androidx.annotation.IntDef; // 定义取值范围 @IntDef(value = { LogLevel.VERBOSE, LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR }) // 指定注解保留策略(SOURCE表示仅编译期有效) @Retention(RetentionPolicy.SOURCE) // 定义自定义注解 public @interface LogLevelAnnotation { int value() default LogLevel.INFO; // 默认值 } ``` #### 3. **使用注解限定参数** ```java public void setLogLevel(@LogLevelAnnotation int level) { // 方法实现... } // 正确用法 setLogLevel(LogLevel.DEBUG); // 错误用法(编译期警告) setLogLevel(99); // 超出限定范围的值 ``` #### 关键说明: 1. **`@IntDef` 元注解** 指定允许的整数值集合,结合常量定义实现范围限定[^2][^4]。 2. **保留策略 `@Retention(SOURCE)`** 注解仅在源码级别有效,不增加运行时开销[^2][^3]。 3. **参数命名规则** 若唯一参数名为 `value`,使用时可直接赋值:`@LogLevelAnnotation(LogLevel.ERROR)`[^1]。 4. **替代枚举的优势** 避免枚举的内存开销(尤其在Android开发中),同时保持类型安全[^4]。 #### 完整示例: ```java // 定义常量 public class NetworkType { public static final int WIFI = 1; public static final int MOBILE = 2; public static final int ETHERNET = 3; } // 自定义注解 @IntDef({NetworkType.WIFI, NetworkType.MOBILE, NetworkType.ETHERNET}) @Retention(RetentionPolicy.SOURCE) public @interface NetworkTypeAnnotation {} // 使用注解 public void connect(@NetworkTypeAnnotation int type) { switch (type) { case NetworkType.WIFI: // 处理WiFi连接 break; case NetworkType.MOBILE: // 处理移动网络 break; } } ``` > **注意事项**: > - 此方案依赖IDE或编译器的静态检查(如Lint) > - 运行时无强制验证,需配合代码规范使用[^2][^4] > - 支持 `@StringDef` 实现字符串限定(原理相同) --- ### 相关问题 1. 如何在运行时获取注解参数值? 2. `@IntDef` 和枚举类型在性能上有何差异? 3. 如何为自定义注解设置多个参数? 4. 注解的 `@Target` 元注解有哪些使用场景? 5. 如何在Kotlin中实现相同的参数限定? [^1]: Java - 自定义注解。规则2:如果注解含有配置参数,则必须使用指定参数值。格式:“参数名 = 参数值”,如果只有一个参数成员,且名称为value,可以省略“value=” [^2]: Java学习 使用注解将参数的值限定。使用@IntDef限定整数值范围,@Retention(SOURCE)表示注解仅在编译期有效 [^3]: Java中的注解,自定义注解。元注解用于修饰其他注解 [^4]: Java参数注解。使用注解替代枚举限定参数值,避免内存开销
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值