引言:开发中有注释,注释和注解有没有关系呢?其实两个是有关系的,只是针对的对象不一样,注释是给开发者看的;而注解是给程序看的,程序可以根据不同的注解来做不同的业务逻辑。
1. 什么是注解?
注解(Annotation) 是 JDK 5 引入的语法,本质是给代码贴标签------标签本身不影响代码执行,但能被编译器、框架、运行时反射机制读取并做出相应处理。
一句话:注解是**"代码的使用说明书"**------告诉别人"这段代码是用来干嘛的、什么时候用"。
Java 内置了三类常用注解:
| 注解 | 作用 |
|---|---|
@Override |
告诉编译器"我要重写父类方法" |
@Deprecated |
告诉别人"这个方法已过时,不推荐用" |
@SuppressWarnings |
告诉编译器"别再警告了",注解中的参数参考附录表A |
但更强大的,是自定义注解。
2. 为什么需要注解?
举个现实例子:
去超市买东西,每件商品都贴着标签------"生产日期 2026-01-01"、"保质期 12 个月"、"产地:云南"。这些标签不改变商品本身,但消费者和超市系统可以通过标签快速识别商品属性。
注解就是代码的"商品标签":
-
商品 = 类、方法、字段
-
标签 = 注解
-
看标签的人 = 编译器 / 框架 / 反射机制
以前写代码要在 XML 里配置这些"标签"(如 Spring 早期),现在直接写在代码上,更直观。
3. 代码示例:自定义一个"员工级别"注解
① 定义注解
Java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) // 注解保留到运行时
@Target(ElementType.FIELD) // 只能贴在字段上
public @interface Level {
int value(); // 必填:级别数字
String desc() default "普通员工"; // 选填:级别描述
}
② 使用注解
Java
public class Employee {
@Level(value = 5, desc = "高级工程师")
private String engineer;
@Level(value = 8, desc = "技术总监")
private String director;
@Level(3) // 只有 value 时可以省略名字
private String junior;
}
③ 通过反射读取注解
Java
public class TestAnnotation {
public static void main(String[] args) {
Class<?> clazz = Employee.class;
for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
Level level = field.getAnnotation(Level.class);
if (level != null) {
System.out.println(field.getName() + " → 级别 "
+ level.value() + "(" + level.desc() + ")");
}
}
}
}
④ 测试结果
Plain
engineer → 级别 5(高级工程师)
director → 级别 8(技术总监)
junior → 级别 3(普通员工)
4. 元注解:注解的"注解"
@Retention 和 @Target 叫元注解(meta-annotation),用来"注解"注解本身。
@Retention:注解能活多久
| 取值 | 含义 | 典型用途 |
|---|---|---|
SOURCE |
只在源码期,编译后丢弃 | 给 IDE 看(如 @Override) |
CLASS |
编译到 class 文件,运行时不加载 | 默认值,框架字节码增强用 |
RUNTIME |
运行时也能读到 | 反射读取、自定义框架 |
@Target:注解能贴在哪
| 取值 | 可贴位置 |
|---|---|
FIELD |
字段 |
METHOD |
方法 |
TYPE |
类、接口、枚举 |
PARAMETER |
方法参数 |
CONSTRUCTOR |
构造方法 |
LOCAL_VARIABLE |
局部变量 |
可以多选:
@Target({ElementType.FIELD, ElementType.METHOD})
5. 反例对比:XML 配置 vs 注解配置
以"在 Spring 中注册一个 Bean"为例。
❌ 反例:纯 XML 配置(Spring 早期)
XML
<!-- applicationContext.xml -->
<beans>
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.UserDao"/>
</beans>
痛点:Bean 类改了名字/属性,XML 必须同步改,否则运行不起来。配置文件越来越臃肿。
✅ 正例:注解配置(Spring Boot 现在)
Java
@Service // 直接在类上贴标签:我是 Service
public class UserService {
@Autowired // 直接在字段上贴:帮我注入 userDao
private UserDao userDao;
}
效果 :Bean 的元信息和代码写在同一处,改一处就生效,IDE 还能跳转提示。
6. 扩展场景:注解在框架中的应用
① Spring 全家桶
| 注解 | 作用 |
|---|---|
@Component / @Service / @Repository |
把类标记为 Spring 容器管理的 Bean |
@Autowired |
自动注入依赖 |
@RequestMapping / @GetMapping |
把 URL 路径映射到方法 |
@Transactional |
给方法加事务控制 |
原理:Spring 启动时扫描所有类,反射读取这些注解,自动建对象、注入依赖、注册路由。
② MyBatis
Java
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(int id);
@Insert("INSERT INTO user(name) VALUES(#{name})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(User user);
}
没写实现类 ,但 MyBatis 运行时通过动态代理 + 反射读注解,自动帮你生成 SQL 执行代码。
总结:注解让框架从"读 XML 配置"变成"读代码标签",开发效率大幅提升。
7. 什么时候用注解?
✅ 用 :框架配置(Spring/MyBatis)、数据校验(@NotNull)、权限控制(@RequiresPermission)、单元测试标记(@Test)、自定义业务标签(如日志埋点)。
❌ 不用:复杂业务逻辑(注解本质只是数据,不适合装复杂规则)、需要频繁修改的配置(写死在注解里改一次要重新编译)。
👋 一句话总结注解:注解是"给代码贴标签",编译器和框架通过反射读标签,实现"配置即代码"。
三、课堂小结
| 知识点 | 核心一句话 | 关键代码/语法 |
|---|---|---|
| 自定义注解 | 用 @interface 定义 |
@Retention + @Target |
| 元注解 | 注解的"注解" | RUNTIME + FIELD |
| 框架应用 | 反射读注解 → 自动配置 | @Component、@Select |
记忆口诀:
泛型:编译器帮你管类型
注解:框架帮你读标签