自定义注解详解

发布于:2025-05-20 ⋅ 阅读:(17) ⋅ 点赞:(0)

自定义注解详解


1. 定义自定义注解

自定义注解通过@interface关键字定义,需结合元注解(如@Target@Retention)控制其行为。

语法模板
@Retention(RetentionPolicy.RUNTIME)   // 注解保留到运行时
@Target(ElementType.METHOD)           // 注解用于方法
public @interface MyAnnotation {
    // 注解属性(类似无参方法)
    String value() default "默认值";    // 默认值可选
    int priority() default 1;
    Class<?> targetClass() default Object.class;
}
核心元注解说明
  • @Retention

    • RetentionPolicy.SOURCE:仅保留在源码中(如Lombok的@Getter)。
    • RetentionPolicy.CLASS:保留到字节码文件,但运行时不可见(默认)。
    • RetentionPolicy.RUNTIME:运行时可通过反射读取(如Spring的@Autowired)。
  • @Target
    指定注解可应用的位置,支持多个目标(用数组表示):

    @Target({ElementType.METHOD, ElementType.FIELD})
    
  • @Documented
    注解信息包含在Javadoc中。

  • @Inherited
    允许子类继承父类上的注解(仅对类注解有效)。


2. 注解属性规则
  • 类型限制
    属性类型只能是基本类型StringClass枚举其他注解或这些类型的数组。 不能是Object类型。

    // 合法属性
    int id();
    String[] tags();
    Class<?> target();
    RetentionPolicy policy(); // 枚举类型
    
  • 默认值
    通过default关键字指定,未指定默认值的属性在使用时必须显式赋值。

    String value() default "unknown";
    
  • 单值属性
    若属性名为value且是唯一需要赋值的属性,可省略属性名:

    @MyAnnotation("直接赋值value")
    

3. 使用自定义注解
示例:标记需要权限验证的方法
// 定义权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
    String[] roles();       // 必须显式赋值
    String description() default "";
}

// 使用注解
public class UserService {
    @RequiresPermission(roles = {"admin", "superuser"}, description = "删除用户权限")
    public void deleteUser(String userId) {
        // 删除用户逻辑
    }
}
示例:标记API接口版本
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ApiVersion {
    String value();  // 版本号,如"v1.2"
}

@ApiVersion("v1.0")
public class UserController {
    // 控制器方法
}

4. 处理自定义注解
(1) 运行时反射处理

通过反射读取注解信息,动态执行逻辑(如权限校验、日志记录)。

示例:权限校验拦截器
public class PermissionInterceptor {
    public static void checkPermission(Method method) {
        if (method.isAnnotationPresent(RequiresPermission.class)) {
            RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);
            String[] requiredRoles = annotation.roles();
            String currentRole = getCurrentUserRole(); // 模拟获取当前用户角色
            
            boolean hasPermission = false;
            for (String role : requiredRoles) {
                if (role.equals(currentRole)) {
                    hasPermission = true;
                    break;
                }
            }
            
            if (!hasPermission) {
                throw new SecurityException("权限不足: 需要角色 " + Arrays.toString(requiredRoles));
            }
        }
    }

    private static String getCurrentUserRole() {
        return "user"; // 模拟返回当前用户角色
    }
}

// 测试代码
public class Main {
    public static void main(String[] args) throws NoSuchMethodException {
        Method deleteMethod = UserService.class.getMethod("deleteUser", String.class);
        PermissionInterceptor.checkPermission(deleteMethod); // 抛出SecurityException
    }
}
(2) 编译时注解处理器(APT)

通过继承AbstractProcessor生成额外代码(如Lombok)。

示例:生成Getter方法(简化版)
@SupportedAnnotationTypes("com.example.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
public class GetterProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement annotation : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
                // 生成Getter方法代码(具体实现略)
                System.out.println("为字段生成Getter: " + element.getSimpleName());
            }
        }
        return true;
    }
}

5. 实际应用场景
场景 示例
权限控制 方法级注解标记访问所需角色(如Spring Security的@PreAuthorize)。
日志切面 注解标记需要记录日志的方法(结合AOP实现)。
数据验证 字段注解定义校验规则(如Hibernate Validator的@NotNull)。
API文档生成 注解标记接口说明(如Swagger的@ApiOperation)。
依赖注入 自定义@Inject注解实现简易IoC容器。

6. 高级特性
(1) 重复注解(Java 8+)
@Repeatable(Validations.class)
public @interface Validation {
    String rule();
}

public @interface Validations {
    Validation[] value();
}

// 使用
@Validation(rule = "notNull")
@Validation(rule = "maxLength:100")
public class User {}
(2) 类型注解(Java 8+)
public class Data {
    List<@NonNull String> items; // 类型注解
}

7. 常见问题
  1. 注解属性必须赋值
    若未指定default值,使用时必须显式赋值。
  2. 注解继承不生效
    @Inherited仅对类注解有效,方法/字段注解默认不继承。
  3. 反射性能开销
    频繁反射读取注解可能影响性能,建议缓存结果。

总结

自定义注解是Java元编程的核心工具,通过合理设计注解,可以大幅提升代码的可维护性和框架的灵活性。掌握其定义、使用和处理方式,是进阶Java开发的必备技能!


网站公告

今日签到

点亮在社区的每一天
去签到