Java自定义注解创建详解

张开发
2026/4/6 22:40:32 15 分钟阅读

分享文章

Java自定义注解创建详解
一、什么是自定义注解注解Annotation​ 是一种元数据用来为代码提供额外信息但本身不改变代码逻辑。Java 内置注解如OverrideDeprecatedSuppressWarnings而自定义注解​ 就是开发者自己定义的注解类型用于编译期检查框架配置如 Spring、MyBatis运行时反射处理自动生成代码二、定义一个注解的基本语法public interface 注解名 { 类型 元素名() default 默认值; }注解本质上是一个接口 注解的方法称为元素Element三、元注解必须掌握元注解用来修饰注解的注解。Java 提供了 5 个标准元注解元注解作用Target注解可以用在什么地方Retention注解保留到哪个阶段Documented是否生成到 JavaDocInherited是否允许子类继承Repeatable是否可重复注解1. Target常用Target(ElementType.METHOD)常见取值TYPE 类/接口METHOD 方法FIELDPARAMETERCONSTRUCTORANNOTATION_TYPE2. Retention非常重要Retention(RetentionPolicy.RUNTIME)RetentionPolicy说明SOURCE只在源码中编译后丢弃CLASS编译进 class但运行时不可见RUNTIME运行时可通过反射读取 ✅如果你想在运行期解析注解必须用 RUNTIME四、自定义一个简单注解示例示例 方法级别的日志注解1. 定义注解import java.lang.annotation.*; Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface Log { String value() default ; boolean enable() default true; }说明value()注解的默认属性default默认值支持多个参数2. 使用注解public class UserService { Log(value 查询用户, enable true) public void queryUser() { System.out.println(查询用户逻辑); } Log(删除用户) public void deleteUser() { System.out.println(删除用户逻辑); } }如果只有一个参数且叫value可以省略value3. 运行时解析注解反射import java.lang.reflect.Method; public class AnnotationTest { public static void main(String[] args) throws Exception { Class? clazz UserService.class; for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(Log.class)) { Log log method.getAnnotation(Log.class); System.out.println(方法名 method.getName()); System.out.println(日志描述 log.value()); System.out.println(是否启用 log.enable()); } } } }输出示例方法名queryUser 日志描述查询用户 是否启用true五、自定义注解 AOP 权限校验1项目结构推荐com.example.demo ├── DemoApplication.java ├── annotation │ └── RequiresRole.java ├── aop │ └── RoleAspect.java ├── service │ └── OrderService.java └── controller └── OrderController.java2引入依赖Mavendependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies3自定义注解核心RequiresRole.javapackage com.example.demo.annotation; import java.lang.annotation.*; Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface RequiresRole { /** * 需要的角色 */ String value(); }关键点RUNTIMEAOP 才能反射拿到用在方法​ 上4AOP 切面重点RoleAspect.javapackage com.example.demo.aop; import com.example.demo.annotation.RequiresRole; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; Aspect Component public class RoleAspect { // 当前登录用户模拟 private static final String CURRENT_USER_ROLE USER; Around(annotation(com.example.demo.annotation.RequiresRole)) public Object checkRole(ProceedingJoinPoint pjp) throws Throwable { // 1. 获取方法签名 MethodSignature signature (MethodSignature) pjp.getSignature(); // 2. 获取方法上的注解 RequiresRole requiresRole signature.getMethod().getAnnotation(RequiresRole.class); // 3. 权限校验 if (!CURRENT_USER_ROLE.equals(requiresRole.value())) { throw new RuntimeException(权限不足需要角色 requiresRole.value()); } // 4. 放行 return pjp.proceed(); } }这里做了什么拦截所有带RequiresRole的方法从注解中读取值和当前用户角色比较不通过直接抛异常5业务层使用注解OrderService.javapackage com.example.demo.service; import com.example.demo.annotation.RequiresRole; import org.springframework.stereotype.Service; Service public class OrderService { RequiresRole(ADMIN) public String deleteOrder(Long orderId) { return 订单 orderId 已删除; } RequiresRole(USER) public String queryOrder(Long orderId) { return 查询订单 orderId; } }6Controller 调用OrderController.javapackage com.example.demo.controller; import com.example.demo.service.OrderService; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/order) public class OrderController { private final OrderService orderService; public OrderController(OrderService orderService) { this.orderService orderService; } GetMapping(/delete/{id}) public String delete(PathVariable Long id) { return orderService.deleteOrder(id); } GetMapping(/query/{id}) public String query(PathVariable Long id) { return orderService.queryOrder(id); } }7启动类DemoApplication.javaSpringBootApplication EnableAspectJAutoProxy public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }EnableAspectJAutoProxy必须加8运行结果演示1. 访问有权限GET /order/query/1返回查询订单 12. 访问无权限GET /order/delete/1控制台 / 返回RuntimeException: 权限不足需要角色ADMIN六、注解支持数组类型1 改造注解支持数组RequiresRoles.javaTarget(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface RequiresRoles { /** * 允许访问的角色列表 */ String[] value(); }关键变化String[] value();2 改造 AOP 切面RoleAspect.javaAspect Component public class RoleAspect { // 模拟当前登录用户角色 private static final String CURRENT_USER_ROLE USER; Around(annotation(com.example.demo.annotation.RequiresRoles)) public Object checkRole(ProceedingJoinPoint pjp) throws Throwable { MethodSignature signature (MethodSignature) pjp.getSignature(); RequiresRoles requiresRoles signature.getMethod().getAnnotation(RequiresRoles.class); // 判断是否包含当前用户角色 boolean hasPermission Arrays.asList(requiresRoles.value()) .contains(CURRENT_USER_ROLE); if (!hasPermission) { throw new RuntimeException( 权限不足所需角色 Arrays.toString(requiresRoles.value()) ); } return pjp.proceed(); } }核心逻辑Arrays.asList(roles).contains(currentRole)3 业务方法中使用OrderService.javaService public class OrderService { RequiresRoles({ADMIN, OPS}) public String deleteOrder(Long orderId) { return 订单 orderId 已删除; } RequiresRoles(USER) public String queryOrder(Long orderId) { return 查询订单 orderId; } }4 测试结果当前角色方法结果USERdeleteOrder权限不足ADMINdeleteOrder成功OPSdeleteOrder成功USERqueryOrder成功七、升级版支持 AND / OR 逻辑进阶1注解定义public interface RequiresRoles { String[] value(); Logical logical() default Logical.OR; }public enum Logical { AND, OR }2AOP 判断Logical logical requiresRoles.logical(); ListString requiredRoles Arrays.asList(requiresRoles.value()); if (logical Logical.AND) { // 包含所有角色 } else { // 包含任一角色 }

更多文章