自定义注解

自定义注解的核心分两步:定义注解(用 @interface 声明)和使用注解(通过反射或 AOP 读取注解信息并执行逻辑)。注解本身只是“标记”,不包含业务逻辑,真正干活的是读取它的代码。

第一步:定义注解

用 @interface 关键字声明,配合元注解(@Target、@Retention 等)限定它的用法和生命周期。

import java.lang.annotation.*;

@Target(ElementType.METHOD)           // 只能标在方法上
@Retention(RetentionPolicy.RUNTIME)   // 运行时保留,反射才能读到
@Documented                           // 生成 Javadoc 时包含该注解
public @interface AdminOnly {
    String value() default "";        // 注解属性,带默认值
    int level() default 1;            // 可以有多个属性
}
元注解作用
@Target限定注解能标在哪里:METHOD、FIELD、TYPE、PARAMETER、CONSTRUCTOR等
@Retention注解保留到什么时候:SOURCE(编译丢弃)、CLASS(默认,类文件保留但运行时不可见)、RUNTIME(运行时可通过反射读取)
@Documented生成 Javadoc 时包含该注解
@Inherited子类是否继承父类的该注解(只对类注解有效)

要能被反射或 AOP 读取,@Retention 必须是 RUNTIME,这是最常见的坑。

第二步:使用注解

public class UserService {
@AdminOnly(value = “删除用户”, level = 2)
public void deleteUser(Long userId) {
// …
}
}

第三步:读取注解并执行逻辑

注解标记了,但不会自动生效,需要有代码去“读”它。两种常见方式:

方式一:反射读取(适合普通方法、字段、类)

Method method = UserService.class.getMethod("deleteUser", Long.class);
if (method.isAnnotationPresent(AdminOnly.class)) {
    AdminOnly anno = method.getAnnotation(AdminOnly.class);
    System.out.println("操作:" + anno.value() + ",级别:" + anno.level());
    // 在这里执行权限校验等逻辑
}

反射读取适合在框架初始化、工具类、测试等场景手动调用。

方式二:AOP 切面读取(适合 Spring 项目,自动拦截)

这是实际项目中最常用的方式——定义注解 + 写切面,方法被调用时自动触发逻辑。

@Aspect
@Component
public class AdminOnlyAspect {

    @Before("@annotation(adminOnly)")
    public void check(JoinPoint jp, AdminOnly adminOnly) {
        // adminOnly 就是目标方法上的注解实例,可以读属性
        Long userId = (Long) jp.getArgs()[0];
        if (!PermissionService.isAdmin(userId)) {
            throw new PermissionDeniedException("需要管理员权限:" + adminOnly.value());
        }
    }
}

@annotation(adminOnly) 切点会匹配所有标注了 @AdminOnly 的方法,并把注解实例绑定到切面方法参数 adminOnly 上。这样只要方法加了注解,切面就自动生效,不需要在每个方法里手写校验。

几个关键注意点

  1. @Retention(RUNTIME) 必须加:否则反射和 AOP 都读不到注解,注解等于白写。
  2. 注解属性只能是基本类型、String、Class、枚举、注解或它们的数组,不能是普通对象。
  3. 注解本身不执行逻辑:它只是元数据,必须有反射或 AOP 代码去读取并执行。很多人以为加了注解就自动生效,其实缺了“读取”这一步。
  4. AOP 只拦截 Spring Bean 的外部调用:同类内部 this. 调用不经过代理,切面不生效。
  5. 注解属性用 default 给默认值,使用时可以省略,代码更简洁。
  6. 组合注解:可以把多个注解组合成一个自定义注解(如 Spring 的 @SpringBootApplication 就是组合注解),用 @AliasFor 处理属性别名。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇