5

前言

在现代 Java 开发中,注解是我们开发中最常用的,在使用springboot进行开发的时候,都是面向注解式开发,内置了我们开发中常用的注解。有时候我们会想着自己去自定义下注解,在开发过程中看到别人写的项目中也常见自定义注解。这篇文章将介绍自定义注解的概念,并通过一个具体的案例展示其实际应用。

image.png

什么是自定义注解?

自定义注解就是开发者自己定义的一种“标签”,用来标记代码中的某些部分。通过这些标签,我们可以在程序运行的时候做一些特殊的处理,比如检查某个方法的执行时间、验证数据的正确性等。自定义注解可以帮助我们减少重复的代码,提高开发效率。主要的用途就在于当我们在处理一些重覆的操作的时候,我们可以通过自定义注解,根据需求对这些操作进行处理

元注解:

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
 这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。

这些注解都是 Java 中用于定义自定义注解行为的元注解(Meta-Annotation),它们决定了自定义注解的适用范围、生命周期、是否包含在 Javadoc 中等。以下是每个元注解的详细解释:

1. @Target

@Target 用于指定自定义注解可以应用的程序元素类型。可以限制自定义注解只能用于类、方法、字段等特定的地方。其常用的取值包括:

  • ElementType.TYPE:可以用在类、接口、枚举上。
  • ElementType.FIELD:可以用在字段(包括枚举常量)上。
  • ElementType.METHOD:可以用在方法上。
  • ElementType.PARAMETER:可以用在方法参数上。
  • ElementType.CONSTRUCTOR:可以用在构造函数上。
  • ElementType.LOCAL_VARIABLE:可以用在局部变量上。
  • ElementType.ANNOTATION_TYPE:可以用在注解类型上。
  • ElementType.PACKAGE:可以用在包声明上。
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    // 注解属性
}

在这个例子中,MyAnnotation 注解只能用于方法。

2. @Retention

@Retention 用于指定自定义注解的保留策略,即该注解在生命周期中的哪个阶段被保留。取值包括:

  • RetentionPolicy.SOURCE:注解只保留在源代码中,编译时被丢弃。
  • RetentionPolicy.CLASS:注解保留在字节码文件中,但在运行时不可见。是默认值。
  • RetentionPolicy.RUNTIME:注解在运行时保留,可以通过反射机制读取。这种方式最常用,适合需要在运行时处理的注解。
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 注解属性
}

在这个例子中,MyAnnotation 注解在运行时仍然有效,可以通过反射获取。

3. @Documented

@Documented 是一个标记注解(没有属性),表示使用这个注解的元素应当被 javadoc 或类似工具文档化。默认情况下,注解是不会被包括在 javadoc 中的,使用 @Documented 可以将其包含进去。

示例:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {
    String message() default "This method needs validation";
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    /**
     * 这个需要权限
     */
    String value() default "USER"; // 权限名称
}


public class UserService {
    @Validate(message = "User registration needs validation")
    public void registerUser(String username, String password) {
        // 用户注册逻辑
    }
    
    @RequirePermission("admin")
    public void getByIdUser(Long id) {
    }
}

image.png

4. @Inherited

@Inherited 是一个标记注解,表示这个注解可以被自动继承。具体来说,当某个类使用了带有 @Inherited 的注解时,如果该类的子类没有显式地声明该注解,那么子类将自动继承父类的该注解。

注意:

@Inherited 仅适用于类级别的注解,对方法、字段等无效。
继承的注解不会在子类中显示,但可以通过反射获取。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
    // 注解属性
}

@MyAnnotation
public class ParentClass {
}

public class ChildClass extends ParentClass {
}

自定义注解案例

使用自定义注解实现权限认证。该示例包括自定义注解、权限检查切面、控制器和简单的用户权限获取逻辑

1. 创建自定义注解

package com.yunzhi.studyAnnotaion.annotaion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value(); // 权限名称
}

2. 创建权限检查切面

import com.yunzhi.studyAnnotaion.annotaion.RequirePermission;
import com.yunzhi.studyAnnotaion.exception.AccessDeniedException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PermissionAspect {

    @Before("@annotation(requirePermission)")
    public void checkPermission(RequirePermission requirePermission) {
        String requiredPermission = requirePermission.value();

        String userPermission = getCurrentUserPermission();

        if (!hasPermission(userPermission, requiredPermission)) {
            throw new AccessDeniedException("No permission to access this resource");
        }
    }

    private String getCurrentUserPermission() {
        return "USER"; 
    }

    private boolean hasPermission(String userPermission, String requiredPermission) {
        return userPermission.equals(requiredPermission);
    }
}

3. 创建控制器

package com.yunzhi.studyAnnotaion.controller;

import com.yunzhi.studyAnnotaion.annotaion.RequirePermission;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/admin")
    @RequirePermission("ADMIN")
    public String adminEndpoint() {
        return "Welcome, admin!";
    }

    @GetMapping("/user")
    @RequirePermission("USER")
    public String userEndpoint() {
        return "Welcome, user!";
    }
}

测试和运行

访问 /api/user:具有USER权限的用户可以访问。

image.png

访问 /api/admin:只有具有ADMIN权限的用户可以访问。

image.png

到此我们就可以通过自定义注解实现一个简单的权限认证功能


kexb
519 声望16 粉丝