# 第03天:Java注解基础 ## 📚 今日目标 1. 理解Java注解的概念 2. 学会自定义注解 3. 理解注解的元注解 4. 实现@DBRouter注解 --- ## 🎯 知识点1:什么是注解? ### 生活中的例子 **标签**: - 商品上的标签:价格、产地、保质期 - 代码上的标签:@Override、@Deprecated **注解的作用**: - 给代码添加元数据(metadata) - 告诉编译器、框架如何处理代码 - 运行时可以通过反射读取 ### Java内置注解 ```java @Override // 标记方法重写父类方法 @Deprecated // 标记方法已过时 @SuppressWarnings("unchecked") // 抑制警告 ``` --- ## 🎯 知识点2:注解的元注解 ### 元注解(Meta-Annotation) **定义**:用来定义注解的注解 ### @Target:指定注解可以用在哪里 ```java @Target(ElementType.METHOD) // 只能用在方法上 @Target(ElementType.TYPE) // 只能用在类上 @Target({ElementType.METHOD, ElementType.TYPE}) // 可以用在方法和类上 ``` **ElementType枚举值**: - `TYPE`:类、接口、枚举 - `METHOD`:方法 - `FIELD`:字段 - `PARAMETER`:参数 - `CONSTRUCTOR`:构造函数 - `LOCAL_VARIABLE`:局部变量 - `ANNOTATION_TYPE`:注解类型 - `PACKAGE`:包 ### @Retention:指定注解保留到什么时候 ```java @Retention(RetentionPolicy.SOURCE) // 只在源码中,编译后丢弃 @Retention(RetentionPolicy.CLASS) // 编译到class文件,运行时不可用 @Retention(RetentionPolicy.RUNTIME) // 运行时可用(可以通过反射读取) ``` **为什么用RUNTIME?** - 我们需要在运行时读取注解 - 通过反射获取注解信息 - 根据注解信息执行路由逻辑 ### @Documented:生成JavaDoc ```java @Documented // 注解信息会包含在JavaDoc中 ``` ### @Inherited:可以继承 ```java @Inherited // 子类会继承父类的注解 ``` --- ## 🛠️ 实践任务1:创建@DBRouter注解 ### 步骤1:创建注解文件 在 `src/main/java/cn/bugstack/middleware/db/router/annotation/` 目录下创建 `DBRouter.java`: ```java package cn.bugstack.middleware.db.router.annotation; import java.lang.annotation.*; /** * 数据库路由注解 * * 使用方式: * @DBRouter(key = "userId") * public void queryUser(User user) { * // 方法实现 * } * * @author 小傅哥 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface DBRouter { /** * 分库分表字段 * 用于指定路由键,如:userId、orderId等 * * @return 路由键字段名 */ String key() default ""; } ``` ### 代码解释 1. **@Documented**:生成JavaDoc时会包含这个注解 2. **@Retention(RetentionPolicy.RUNTIME)**:运行时可用,可以通过反射读取 3. **@Target({ElementType.METHOD, ElementType.TYPE})**:可以用在方法和类上 4. **String key() default ""**:注解属性,默认值为空字符串 ### 使用示例 ```java // 方法级别使用 @DBRouter(key = "userId") public User queryUser(User user) { return userMapper.selectById(user.getUserId()); } // 类级别使用 @DBRouter(key = "userId") public class UserService { // ... } ``` --- ## 🛠️ 实践任务2:创建@DBRouterStrategy注解 ### 步骤1:创建注解文件 在同一个目录下创建 `DBRouterStrategy.java`: ```java package cn.bugstack.middleware.db.router.annotation; import java.lang.annotation.*; /** * 数据库路由策略注解 * * 用于类级别,指定是否分表 * * @author 小傅哥 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DBRouterStrategy { /** * 是否分表 * true:需要分表,SQL中的表名会被替换 * false:只分库,不分表 * * @return 是否分表 */ boolean splitTable() default false; } ``` ### 使用示例 ```java @DBRouterStrategy(splitTable = true) public class UserMapper { @DBRouter(key = "userId") public User selectById(Long userId) { // SQL: SELECT * FROM user WHERE id = ? // 会被替换为: SELECT * FROM user_01 WHERE id = ? } } ``` --- ## 🎓 知识点拓展 ### 拓展1:注解属性的类型 **允许的类型**: - 基本类型(int, long, boolean等) - String - Class - 枚举 - 注解 - 以上类型的数组 **例子**: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); // String类型 int count() default 0; // int类型,有默认值 Class clazz(); // Class类型 ElementType[] types(); // 数组类型 DBRouter router(); // 注解类型 } ``` ### 拓展2:注解的默认值 ```java public @interface MyAnnotation { String value() default ""; // 有默认值,使用时可以不写 int count(); // 没有默认值,使用时必须写 } // 使用 @MyAnnotation(count = 10) // value使用默认值 @MyAnnotation(value = "test", count = 10) // 都指定 ``` ### 拓展3:通过反射读取注解 ```java // 获取方法上的注解 Method method = UserService.class.getMethod("queryUser", User.class); DBRouter annotation = method.getAnnotation(DBRouter.class); if (annotation != null) { String key = annotation.key(); // 获取路由键 System.out.println("路由键: " + key); } // 获取类上的注解 DBRouterStrategy strategy = UserService.class.getAnnotation(DBRouterStrategy.class); if (strategy != null) { boolean splitTable = strategy.splitTable(); System.out.println("是否分表: " + splitTable); } ``` ### 拓展4:注解的继承 ```java @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); } @MyAnnotation("parent") public class Parent { } // Child会继承Parent的@MyAnnotation注解 public class Child extends Parent { } ``` --- ## ✅ 今日检查清单 - [ ] 理解了注解的概念和作用 - [ ] 理解了元注解的含义 - [ ] 创建了@DBRouter注解 - [ ] 创建了@DBRouterStrategy注解 - [ ] 理解了@Target和@Retention的作用 - [ ] 完成了拓展阅读 --- ## 🎯 明日预告 明天我们将学习: - Java反射(Reflection)基础 - 如何通过反射获取方法、字段 - 如何通过反射调用方法和获取字段值 --- ## 💡 思考题 1. 为什么@DBRouter要用@Retention(RetentionPolicy.RUNTIME)? 2. 如果@Target只写ElementType.METHOD,类上能用吗? 3. 注解的属性可以是什么类型? --- ## 📚 参考资源 - [Java注解官方文档](https://docs.oracle.com/javase/tutorial/java/annotations/) - [注解深入理解](https://www.baeldung.com/java-annotations-guide) - [反射和注解](https://www.baeldung.com/java-reflection)