当前位置: 首页 > news >正文

JAVA重点基础、进阶知识及易错点总结(35)注解与反射

🚀 Java 巩固进阶 · 第 35 天

主题:注解与反射结合 —— 让注解"活"起来

📅 进度概览:继昨天学习注解定义之后,今天进入注解的核心应用场景:注解 + 反射。单独的注解只是"标签",只有结合反射读取并处理,才能产生实际逻辑。这是 Spring、MyBatis、JUnit 等框架的底层核心。

💡 核心价值

  • 框架原理:理解 Spring@Autowired注入、MyBatis@Select映射、SpringMVC@RequestMapping绑定的底层实现。
  • 动态逻辑:运行时根据注解信息动态执行代码,实现"配置即代码"。
  • 工具开发:掌握自定义校验框架、简易 IOC 容器、自动化日志切面的实现思路。
  • 面试高频:反射读取注解流程、注解处理器(APT)概念是高级开发常考题。

一、核心原理:反射读取注解信息 🔍

1. 反射获取注解的 API

Class<?>clazz=MyClass.class;// ✅ 1. 判断是否有指定注解if(clazz.isAnnotationPresent(MyAnnotation.class)){// 有注解}// ✅ 2. 获取注解实例(读取属性值)MyAnnotationann=clazz.getAnnotation(MyAnnotation.class);Stringvalue=ann.value();// 读取注解属性// ✅ 3. 获取所有注解Annotation[]allAnns=clazz.getAnnotations();// ✅ 4. 获取方法/字段上的注解Methodmethod=clazz.getMethod("doSomething");LoglogAnn=method.getAnnotation(Log.class);

⚠️关键前提
注解必须用@Retention(RetentionPolicy.RUNTIME)修饰,否则运行时反射读取不到!

2. 处理流程图解

定义注解 (@Log) ↓ 标注在代码上 (UserService.createUser) ↓ 反射扫描类 (Class.forName) ↓ 获取方法 (getDeclaredMethods) ↓ 检查注解 (isAnnotationPresent) ↓ 读取属性 (getAnnotation) ↓ 执行逻辑 (打印日志/校验/注入)

二、实战场景 1:简易参数校验框架 ✅

1. 定义校验注解

// ✅ 定义 @NotNull 注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceNotNull{Stringmessage()default"字段不能为空";}// ✅ 定义 @Range 注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceRange{intmin()default0;intmax()default100;}

2. 标注在实体类上

publicclassUser{@NotNull(message="用户名不能为空")privateStringname;@Range(min=1,max=150)privateintage;// getter/setter}

3. 实现校验器(核心逻辑)

publicclassValidator{publicstaticvoidvalidate(Objectobj)throwsException{Class<?>clazz=obj.getClass();// 1. 获取所有字段for(Fieldfield:clazz.getDeclaredFields()){field.setAccessible(true);Objectvalue=field.get(obj);// 2. 检查 @NotNullif(field.isAnnotationPresent(NotNull.class)){NotNullann=field.getAnnotation(NotNull.class);if(value==null||"".equals(value)){thrownewIllegalArgumentException(ann.message());}}// 3. 检查 @Rangeif(field.isAnnotationPresent(Range.class)){Rangeann=field.getAnnotation(Range.class);if(valueinstanceofInteger){intval=(Integer)value;if(val<ann.min()||val>ann.max()){thrownewIllegalArgumentException(field.getName()+" 超出范围:"+ann.min()+"-"+ann.max());}}}}}}// 测试Useruser=newUser();user.setName("");// 故意留空Validator.validate(user);// 抛异常:用户名不能为空

💡框架关联
这就是Hibernate Validator (JSR-303)和 Spring@Valid的底层原理!


三、实战场景 2:简易 AOP 日志切面 📝

1. 回顾 @Log 注解(第 34 天定义)

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceLog{Stringvalue()default"";Stringlevel()default"INFO";}

2. 实现日志拦截器(模拟 Spring AOP)

publicclassLogInterceptor{// ✅ 核心方法:处理带有 @Log 注解的方法publicstaticvoidinvokeWithLog(Objecttarget,Methodmethod,Object[]args)throwsException{// 1. 检查方法是否有 @Log 注解if(method.isAnnotationPresent(Log.class)){LoglogAnn=method.getAnnotation(Log.class);// 2. 前置日志System.out.println("【日志】开始执行:"+logAnn.value()+" 级别:"+logAnn.level());longstart=System.currentTimeMillis();// 3. 执行目标方法method.setAccessible(true);Objectresult=method.invoke(target,args);// 4. 后置日志longcost=System.currentTimeMillis()-start;System.out.println("【日志】执行完成,耗时:"+cost+"ms");return;}// 无注解,直接执行method.setAccessible(true);method.invoke(target,args);}}// 测试UserServiceservice=newUserService();Methodmethod=service.getClass().getMethod("createUser",String.class);LogInterceptor.invokeWithLog(service,method,newObject[]{"Alice"});

💡框架关联
Spring AOP 的@Aspect本质也是扫描注解 + 动态代理 + 反射调用。


四、实战场景 3:简易依赖注入(IOC 雏形)🔧

1. 定义 @MyAutowired 注解

@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceMyAutowired{Stringvalue()default"";// 指定 Bean 名称}

2. 实现简易容器

publicclassSimpleContainer{privatestaticMap<String,Object>beans=newHashMap<>();// ✅ 注册 BeanpublicstaticvoidregisterBean(Objectbean){Stringname=bean.getClass().getSimpleName();beans.put(name,bean);}// ✅ 注入依赖publicstaticvoidinjectDependencies(Objectbean)throwsException{Class<?>clazz=bean.getClass();for(Fieldfield:clazz.getDeclaredFields()){// 1. 检查是否有 @MyAutowiredif(field.isAnnotationPresent(MyAutowired.class)){field.setAccessible(true);// 2. 获取依赖类型Class<?>fieldType=field.getType();StringbeanName=fieldType.getSimpleName();// 3. 从容器获取 BeanObjectdependency=beans.get(beanName);if(dependency==null){thrownewRuntimeException("未找到依赖 Bean: "+beanName);}// 4. 注入字段field.set(bean,dependency);}}}}

3. 测试注入

// 1. 定义 DaoclassUserDao{publicvoidsave(){System.out.println("UserDao.save");}}// 2. 定义 ServiceclassUserService{@MyAutowiredprivateUserDaouserDao;// 自动注入publicvoidcreateUser(){userDao.save();}}// 3. 启动容器UserDaodao=newUserDao();UserServiceservice=newUserService();SimpleContainer.registerBean(dao);SimpleContainer.registerBean(service);SimpleContainer.injectDependencies(service);// 执行注入// 4. 调用service.createUser();// 输出:UserDao.save(userDao 已被自动赋值)

💡框架关联
这就是Spring IOC 容器的核心逻辑:扫描 → 创建 Bean → 扫描字段 → 注入依赖!


五、🎯 今日实战任务:注解框架实战

任务 1:实现参数校验器

/** * 要求: * 1. 定义 @NotNull 和 @Range 注解 * 2. 创建 User 类,字段上添加注解 * 3. 实现 Validator.validate() 方法 * 4. 测试:传入合法/非法数据,观察是否抛异常 * * 💡 挑战: * - 支持嵌套对象校验(如 User 中有 Address 字段) * - 支持自定义校验规则接口 */

任务 2:实现简易日志切面

/** * 要求: * 1. 使用第 34 天定义的 @Log 注解 * 2. 实现 LogInterceptor.invokeWithLog() 方法 * 3. 统计方法执行耗时 * 4. 测试:调用带注解和不带注解的方法,对比输出 * * 💡 思考: * - 这种方式需要手动调用拦截器,如何用动态代理自动拦截? * - 提示:结合第 33 天的动态代理知识 */

任务 3:实现简易 IOC 容器

/** * 要求: * 1. 定义 @MyAutowired 注解 * 2. 实现 SimpleContainer 的 registerBean 和 injectDependencies * 3. 创建 Service + Dao 结构,测试依赖注入 * 4. 验证:注入后的字段是否不为 null * * 💡 挑战: * - 处理循环依赖问题(A 依赖 B,B 依赖 A) * - 支持按类型注入(不只按名称) */

任务 4:探索 Spring 源码

/** * 要求: * 1. 打开 Spring 项目(或查看在线源码) * 2. 找到 @Autowired 注解定义,查看其元注解 * 3. 找到 ClassUtils 或 ReflectionUtils 类,查看 Spring 如何读取注解 * 4. 记录:Spring 用了哪些反射工具方法? * * 💡 提示: * 关注 AnnotatedElementUtils 类 */

📝 第 35 天 · 核心总结(极简背诵版)

  1. 反射读取注解

    clazz.isAnnotationPresent(Ann.class)clazz.getAnnotation(Ann.class)method.getAnnotation(Ann.class)// ⚠️ 必须 @Retention(RUNTIME)
  2. 三大应用场景

    参数校验:@NotNull/@Range → 反射检查字段值 日志切面:@Log → 反射调用方法前后增强 依赖注入:@Autowired → 反射设置字段值
  3. 框架原理映射

    Hibernate Validator ← 参数校验 Spring AOP ← 日志切面 Spring IOC ← 依赖注入
  4. 性能注意

    • ✅ 缓存反射对象(Class/Method/Field)
    • ✅ 缓存注解信息(避免重复读取)
    • ❌ 避免在高频循环中反射读取注解

明天预告:🛠️Lombok 实战 + 阶段总结—— 提升开发效率的利器!

  • Lombok 核心注解(@Data/@Builder/@Slf4j)
  • Lombok 原理(注解处理器 APT)
  • 注意事项与避坑指南
  • 第 3 阶段总结:设计模式与注解知识脑图

准备好了吗?明天我们用工具"解放双手",并结束本阶段学习! ✨🎉

http://www.jsqmd.com/news/593787/

相关文章:

  • 从零实践:利用aitodpycocotools精准评估小目标检测模型的APvt/APt/APs/APm
  • 四开关Buck-Boost双向DC-DC电源系统全套学习资料:STM32F334C8T6控制下...
  • 别再傻傻分不清了!一文讲透M-LAG与堆叠(iStack/CSS)的核心区别与选型指南
  • 【蓝桥杯】练习题目合集(自用)-4
  • 论文AI率80%+的紧急处理方案,答辩前用得上
  • 基于MATLAB的多种概率分布拟合与KS检验:从GEV到Exponential分布选择与实践
  • JAVA重点基础、进阶知识及易错点总结(36)Lombok 实战 + 阶段总结
  • V4L2框架深度优化指南:如何让你的Linux摄像头驱动性能翻倍?
  • 基于初阶拉格朗日算法的ATC模型的多微网主动配电系统自治优化经济调度GAMS代码
  • Voids
  • Langflow AI平台安全自查清单:从环境配置、CVE-2025-3248到内核后门的全面防护指南
  • 2026届最火的六大降AI率平台实测分析
  • STM32F107单片机驱动Dp83848以太网芯片程序 项目开发用到了Dp83848这一个以...
  • 02_Elasticsearch知识体系之Mapping映射设计与索引建模实战
  • 深入解析build.prop:从基础参数到高级定制指南
  • YOLOv11涨点改进| AAAI 2025 |自研创新首发、特征融合改进篇| 使用TAMoE任务自适应混合专家模块,多专家协同合作,各司其职,助力各种任务的目标检测,图像分割,多模态融合目标检测涨点
  • 05_Elasticsearch知识体系之BM25向量搜索与混合检索实战
  • 2026届必备的五大降AI率神器实际效果
  • 突破企业AI应用开发瓶颈:Awesome-Dify-Workflow无代码解决方案深度剖析
  • 04_Elasticsearch知识体系之ESQL管道查询与JOIN分析实战
  • 一台机器也能玩转StarRocks?手把手教你搭建单机测试环境(附避坑指南)
  • 2025届最火的降重复率网站推荐榜单
  • 06_Elasticsearch知识体系之IngestPipelines数据处理与增强实战
  • 如何从零搭建Cubli_Mini:开源自平衡机器人完整制作指南
  • 内部链接 - -王心雨
  • SMU Debug Tool完全指南:AMD Ryzen硬件调试的终极解决方案
  • 别只盯着TCP!拆解大疆源码里MQTT协议的双通道设计:BASIC与DRC到底有啥区别?
  • AI教程——让每个人都能高效写出好故事
  • 2025届最火的AI学术方案实际效果
  • 别再踩坑了!评测5款开源IM源码后,我为什么最终选了鸽哒(附原生/H5性能对比)