点击上方“程序员蜗牛g”,选择“设为星标”
跟蜗牛哥一起,每天进步一点点
程序员蜗牛g
大厂程序员一枚 跟蜗牛一起 每天进步一点点
33篇原创内容
**
公众号
Apache Commons Lang 提供了一个神器 —— AnnotationUtils。
为什么选择 AnnotationUtils?
传统的注解处理方式通常像这样:
// 查找注解并获取属性MyAnnotation annotation = myClass.getAnnotation(MyAnnotation.class);if (annotation != null) { String value = annotation.value();}
// 遍历父类寻找继承的注解Class<?> clazz = myClass;while (clazz != null) { MyAnnotation ann = clazz.getAnnotation(MyAnnotation.class); if (ann != null) { break; } clazz = clazz.getSuperclass();}
// 比较两个注解boolean isSame = annotation.equals(anotherAnnotation); // 可能失败
核心 API 使用详解
- 查找与获取注解
路径:/src/main/java/com/icoderoad/demo/annotation/AnnotationFinder.java
@RestController@RequestMapping("/api")public class MyController { }
// 支持继承查找RequestMapping mapping = AnnotationUtils.findAnnotation(MyController.class, RequestMapping.class);
即便注解定义在父类上,也能轻松找到。 同样适用于 字段与方法:
public class User { @NotBlank(message = "用户名不能为空") private String username;
@Override @Transactional public String toString() { return username; }}
Field field = User.class.getDeclaredField("username");NotBlank notBlank = AnnotationUtils.getAnnotation(field, NotBlank.class);
Method method = User.class.getMethod("toString");Transactional tx = AnnotationUtils.getAnnotation(method, Transactional.class);
2. 注解属性访问
路径:/src/main/java/com/icoderoad/demo/annotation/AnnotationValueReader.java
@RestController@RequestMapping(value = "/api", name = "userApi")public class UserController { }
// 获取属性RequestMapping mapping = AnnotationUtils.findAnnotation(UserController.class, RequestMapping.class);
String path = AnnotationUtils.getValue(mapping, "value"); // "/api"String name = AnnotationUtils.getValue(mapping, "name"); // "userApi"
// 检查默认值boolean isDefault = AnnotationUtils.isDefaultValue(mapping, "produces"); // true
// 获取所有属性Map<String, Object> attrs = AnnotationUtils.getAnnotationAttributes(mapping);// {value=["/api"], name="userApi", produces="", ...}
3. 注解比较与验证
路径:/src/main/java/com/icoderoad/demo/annotation/AnnotationComparer.java
@RequestMapping("/api")public class ControllerA {}
@RequestMapping("/api")public class ControllerB { }
// 普通比较可能失败Annotation ann1 = ControllerA.class.getAnnotation(RequestMapping.class);Annotation ann2 = ControllerB.class.getAnnotation(RequestMapping.class);
// AnnotationUtils 安全比较boolean equal = AnnotationUtils.equals(ann1, ann2); // trueint hash1 = AnnotationUtils.hashCode(ann1);int hash2 = AnnotationUtils.hashCode(ann2);
用于属性验证时:
@Validatedpublic class User { @Size(min = 3, max = 20) private String username;}
Size size = AnnotationUtils.getAnnotation( User.class.getDeclaredField("username"), Size.class);
if (size != null) { int min = (Integer) AnnotationUtils.getValue(size, "min"); int max = (Integer) AnnotationUtils.getValue(size, "max");}
实战场景演示
场景一:自定义权限注解
路径:/src/main/java/com/icoderoad/demo/security/PermissionAnnotationProcessor.java
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface RequiresPermission { String[] value(); Logical logical() default Logical.AND;}
@Componentpublic class PermissionAnnotationProcessor { public boolean checkPermission(Method method, User user) { RequiresPermission perm = AnnotationUtils.findAnnotation(method, RequiresPermission.class);
if (perm != null) { String[] required = (String[]) AnnotationUtils.getValue(perm, "value"); Logical logic = (Logical) AnnotationUtils.getValue(perm, "logical"); return checkUserPermissions(user, required, logic); } return true; }}
场景二:条件化配置
路径:/src/main/java/com/icoderoad/demo/config/ConfigurationProcessor.java
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ConditionalOnProperty { String value(); String havingValue() default "true";}
public class ConfigurationProcessor { private final Environment env;
public ConfigurationProcessor(Environment env) { this.env = env; }
public boolean shouldProcess(Class<?> configClass) { ConditionalOnProperty condition = AnnotationUtils.findAnnotation(configClass, ConditionalOnProperty.class); if (condition != null) { String key = (String) AnnotationUtils.getValue(condition, "value"); String expected = (String) AnnotationUtils.getValue(condition, "havingValue"); String actual = env.getProperty(key); return expected.equals(actual); } return true; }}
场景三:组合注解与元注解
路径:/src/main/java/com/icoderoad/demo/api/ApiScanner.java
@RestController@RequestMapping("/api/v1")@ResponseBody@CrossOrigin@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface RestApiEndpoint { String value() default "";}
@RestApiEndpoint("users")public class UserController { @GetMapping public List<User> getUsers() { return userService.findAll(); }}
public class ApiScanner { public void scanControllers(Class<?>... classes) { for (Class<?> clazz : classes) { RestApiEndpoint endpoint = AnnotationUtils.findAnnotation(clazz, RestApiEndpoint.class); if (endpoint != null) { String path = (String) AnnotationUtils.getValue(endpoint, "value"); RequestMapping mapping = AnnotationUtils.findAnnotation(clazz, RequestMapping.class); String[] basePaths = (String[]) AnnotationUtils.getValue(mapping, "value"); System.out.println("发现 API: " + basePaths[0] + "/" + path); } } }}
高级玩法与最佳实践
- 重复注解处理:通过
AnnotationUtils.getAnnotationsByType轻松解析@Repeatable注解。 - 属性合并:遍历类层次结构并合并注解属性,常用于继承场景。
- 缓存优化:在频繁调用时对注解进行缓存,减少反射消耗。
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!