答案是注解本质是编译期元数据,运行时靠反射读取;需掌握原理、动手实现(如@Sensitive脱敏)、串联反射/AOP/SPI;警惕@Target/@Retention作用域及属性类型限制。

搞清注解本质,别被“语法糖”带偏
注解不是魔法,它是编译期插入的元数据接口,运行时靠反射读取。面试官问“@Autowired 怎么注入的”,你不能只答“Spring 扫描到就注入了”,得拆开说:
- @Autowired 是一个 @Retention(RUNTIME) 注解,所以 ClassLoader 加载类后还能通过反射拿到
- Spring 启动时遍历所有 Bean,对每个字段/构造器/方法调用 isAnnotationPresent(Autowired.class)
- 如果命中,再用 getDeclaredField().setAccessible(true) 破权限,最后通过 beanFactory.getBean() 查找并赋值
- 整个过程不修改字节码,也不生成新类——纯靠反射+容器管理
手写一个有业务价值的自定义注解
光会抄 @interface 不算数。面试官想看的是:你能不能用注解解决真实痛点。比如日志脱敏:
- 定义 @Sensitive 注解,标注在 DTO 字段上
- 配合 AOP,在 Controller 返回前扫描响应对象,对带该注解的 String 字段做掩码处理(如手机号 → 138****1234)
- 加个 @Target({FIELD}) 和 @Retention(RUNTIME),确保只标字段、运行时可见
- 别忘了在 AOP 切面里用 field.getAnnotation(Sensitive.class) != null 做判断
把注解和反射、代理、SPI 串起来讲
高级题往往跨知识点。比如问:“Spring Boot 的 @ConditionalOnClass 怎么判断类是否存在?”你就得连起来答:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 它本质是 条件化装配,靠的是 Spring 的 Condition 接口
- @ConditionalOnClass 本身是元注解,被 @Conditional(OnClassCondition.class) 修饰
- OnClassCondition 在启动时调用 ClassUtils.isPresent(className, getClass().getClassLoader())
- 而 isPresent 底层就是 try-catch + Class.forName() —— 典型反射用法
- 整个链路:注解声明 → 条件处理器 → 反射加载类 → 决定是否注册 Bean
警惕几个高频翻车点
这些细节一错,立刻暴露原理没吃透:
立即学习“Java免费学习笔记(深入)”;
- @Target(ElementType.TYPE) ≠ 只能标类:它还覆盖 interface、enum、annotation 类型
- RetentionPolicy.CLASS 的注解,无法通过反射获取(getAnnotation 返回 null),但 javap 能看到字节码里有
- 自定义注解属性类型有限制:只能是基本类型、String、Class、枚举、其他注解,或以上类型的数组
- 注解属性没有继承性:子类不会自动继承父类方法上的注解,必须显式用 @Inherited(且仅对类有效,对方法/字段无效)

















