Java注解自定义注解
1. 注解的概念Java 注解Annotation是 JDK 5 引入的一种元数据形式它提供了一种安全的方式将信息与程序元素类、方法、变量、参数、包等关联起来。注解本身不做任何事情它只是提供信息元数据。真正让注解发挥作用的是处理这些注解的工具或框架编译器、开发工具或运行时反射机制来读取和处理这些信息的框架。1.1. 注解的本质标签/标记可以把注解想象成给代码元素贴的便利贴或标签Override// ← 这就是一个标签告诉编译器我打算重写父类方法publicStringtoString(){returnMyClass;}这个 Override 标签本身不会改变 toString() 方法的行为。但编译器会检查这个标签是否正确使用。1.2. 谁在读取注解注解的功能取决于谁在处理它。【编译器】处理的例子处理者Java 编译器功能编译时验证Override// ← 编译器会处理这个注解publicvoidnonExistentMethod(){}// 编译错误因为父类没有这个方法【开发工具】处理的例子处理者IDE如 IntelliJ IDEA、Eclipse功能在代码中显示删除线提醒开发者不要使用Deprecated// ← 开发工具IDE会处理这个注解publicvoidoldMethod(){}【运行时框架】处理的例子处理者Spring 框架功能在应用启动时Spring 扫描这些注解自动注册控制器和路由RestController// ← Spring 框架会处理这个注解publicclassMyController{GetMapping(/hello)// ← Spring 框架会处理这个注解publicStringhello(){returnHello World;}}1.3. 生活中的类比想象一个图书馆书籍 你的代码书上的标签 注解比如新书、“参考书”、“限阅”图书管理员 处理注解的工具/框架标签本身不会改变书的内容但图书管理员看到新书标签就会把它放到新书架看到限阅标签就知道只有特定读者能借阅。2. 注解的功能2.1 注解的三种主要功能场景2.2.1. 编译时检查Compiler-time帮助【编译器】发现错误或警告。例如Override 确保方法正确重写了父类方法。// 功能防止编程错误Overridepublicbooleanequals(Objectobj){// 如果方法签名错了编译器会报错returnsuper.equals(obj);}2.1.2. 编译时代码生成Code Generation根据注解自动生成代码减少样板代码的编写。// Lombok 注解 - 编译时自动生成 getter/setterDatapublicclassUser{privateStringname;privateintage;}// 编译后实际生成了 getName(), setName(), getAge(), setAge() 等方法2.1.3. 运行时反射处理Runtime Reflection通过反射在运行时读取注解信息实现框架的核心功能如 Spring、Hibernate或自定义逻辑。// JUnit 测试 - 运行时测试框架通过反射找到带 Test 的方法并执行TestpublicvoidtestAddition(){assertEquals(4,22);}// Spring 依赖注入 - 运行时Spring框架通过反射找到 Autowired 字段并注入实例AutowiredprivateUserServiceuserService;2.2 使用注解的优势使用注解的优势提高代码可读性 - 清晰表达代码意图减少配置文件 - 将配置信息直接嵌入代码中增强类型安全 - 编译时就能发现问题简化开发 - 自动化处理重复性工作框架集成 - 现代 Java 框架大量依赖注解这里从两个视角对注解 vs 传统方式直观感受注解优势XML 配置 vs 注解配置注解更直观与代码紧密耦合接口/抽象类 vs 注解注解更轻量无需继承关系2.2.1 传统XML配置 vs 注解以生成和注入bean为例传统 XML 配置!-- applicationContext.xml --beaniduserServiceclasscom.example.UserService/beaniduserControllerclasscom.example.UserControllerpropertynameuserServicerefuserService//bean注解配置注解把配置信息直接嵌入到代码中更加直观和类型安全。ServicepublicclassUserService{}ControllerpublicclassUserController{AutowiredprivateUserServiceuserService;}2.2.2 接口/抽象类 vs 注解以缓存为例需求标记某些方法的结果可以被缓存。传统方式使用接口/抽象类step1. 定义接口publicinterfaceCacheable{// 空接口仅作标记}step2. 业务类实现接口publicclassUserServiceimplementsCacheable{publicUsergetUserById(Longid){returnuserRepository.findById(id);}publicListUsergetAllUsers(){returnuserRepository.findAll();}}step3.新建缓存处理器实现缓存逻辑publicclassCacheHandler{publicObjectexecuteWithCache(Objecttarget,StringmethodName,Object...args)throwsException{// 检查目标对象是否实现了 Cacheable 接口if(targetinstanceofCacheable){// 生成缓存key这里简化处理Stringkeytarget.getClass().getSimpleName()_methodName;// 检查缓存ObjectcachedgetFromCache(key);if(cached!null){returncached;}// 执行方法Methodmethodtarget.getClass().getMethod(methodName);Objectresultmethod.invoke(target,args);// 存入缓存putInCache(key,result);returnresult;}else{// 不是 Cacheable直接执行Methodmethodtarget.getClass().getMethod(methodName);returnmethod.invoke(target,args);}}privateObjectgetFromCache(Stringkey){/* ... */}privatevoidputInCache(Stringkey,Objectvalue){/* ... */}}step4. 使用publicclassApplication{publicstaticvoidmain(String[]args)throwsException{UserServiceuserServicenewUserService();CacheHandlercacheHandlernewCacheHandler();// 通过缓存处理器调用方法Useruser(User)cacheHandler.executeWithCache(userService,getUserById,1L);}}传统实现方式有如下问题粒度太粗只能标记整个类无法精确到方法。业务类实现缓存标记接口会导致业务类所有方法都默认被缓存不能标记单个方法调用方式不自然不能直接调用 userService.getUserById(1L) 必须通过特殊的调用方式或复杂的代理机制间接调用破坏了正常的面向对象调用习惯缺乏配置能力无法传递缓存时间、缓存key等配置信息侵入性强业务类被迫实现与业务无关的接口注解方式step1. 定义注解Retention(RetentionPolicy.RUNTIME)Target(ElementType.METHOD)// 关键可以标记在方法上publicinterfaceCacheable{Stringvalue()default;longttl()default3600;// 缓存时间秒}step2. 直接在需要的方法上使用publicclassUserService{Cacheable(ttl1800)// 只缓存这个方法30分钟过期publicUsergetUserById(Longid){returnuserRepository.findById(id);}// 这个方法不缓存publicListUsergetAllUsers(){returnuserRepository.findAll();}Cacheable(ttl7200)// 这个方法也缓存但2小时过期publicUsergetUserByEmail(Stringemail){returnuserRepository.findByEmail(email);}}step3. 处理注解框架层面// AOP 切面处理缓存逻辑AspectComponentpublicclassCacheAspect{Around(annotation(cacheable))publicObjectcacheAdvice(ProceedingJoinPointjoinPoint,Cacheablecacheable)throwsThrowable{// 1. 生成缓存keyStringkeygenerateKey(joinPoint);// 2. 检查缓存是否存在ObjectcachedredisTemplate.opsForValue().get(key);if(cached!null){returncached;}// 3. 执行原方法ObjectresultjoinPoint.proceed();// 4. 存入缓存redisTemplate.opsForValue().set(key,result,cacheable.ttl(),TimeUnit.SECONDS);returnresult;}}step4. 使用方式publicclassApplication{publicstaticvoidmain(String[]args)throwsException{Autowired// 直接注入使用无需特殊处理privateUserServiceuserService;UseruseruserService.getUserById(1L);// 自动缓存调用方式完全自然}}注解的真正威力在于它提供了一种与代码逻辑完全解耦的元数据表达方式。你的业务代码还是纯粹的业务代码缓存、事务、安全等横切关注点通过注解声明框架在运行时读取这些注解并织入相应的逻辑3. 注解的分类3.1. 内置注解Built-in Annotations通用注解Override - 标记方法重写Deprecated - 标记过时的 API类、方法等SuppressWarnings - 抑制编译器警告SafeVarargs - JDK 7新增抑制堆污染警告FunctionalInterface - Java8新增函数式接口如果接口中只有一个抽象方法(可以包含多个 default 方法或多个 static 方法)该接口称为函数式接口元注解Meta-Annotations用于定义其他注解的注解Retention - 指定注解的保留策略RetentionPolicy.SOURCE - 仅在源码中保留编译后丢弃使用场景Override, SuppressWarningsRetentionPolicy.CLASS - 编译时写入 class 文件运行时不可见使用场景字节码分析工具RetentionPolicy.RUNTIME - 运行时保留可通过反射获取使用场景框架配置、AOP、序列化等Target - 指定注解的使用目标ElementType.TYPE - 类、接口、枚举ElementType.METHOD - 方法ElementType.FIELD - 字段ElementType.PARAMETER - 参数等Documented - 包含在 Javadoc 中Inherited - 允许子类继承父类的注解Repeatable - 允许同一位置重复使用Java 83.2 框架注解Spring Framework框架Component, Service, Repository, ControllerAutowired, QualifierRequestMapping, GetMapping, PostMappingConfiguration, BeanJPA/Hibernate框架Entity, TableId, GeneratedValueColumn, OneToMany, ManyToOneJUnit框架Test, Before, AfterBeforeEach, AfterEach (JUnit 5)3.3. 自定义注解自定义注解时理解注解的功能 注解定义 注解处理器就掌握了注解的核心思想。// 1. 定义注解Retention(RetentionPolicy.RUNTIME)Target(ElementType.METHOD)interfaceLoggable{}// 2. 使用注解classCalculator{Loggablepublicintadd(inta,intb){returnab;}publicintmultiply(inta,intb){returna*b;}}// 3. 处理注解这就是让注解发挥作用的地方publicclassAnnotationDemo{publicstaticvoidmain(String[]args)throwsException{CalculatorcalcnewCalculator();Method[]methodsCalculator.class.getDeclaredMethods();for(Methodmethod:methods){if(method.isAnnotationPresent(Loggable.class)){System.out.println(发现可记录日志的方法: method.getName());// 这里可以添加日志逻辑、性能监控等}}}}4. 自定义注解完整DEMO日常开发中使用自定义注解可分为三步step1: 定义注解step2: 处理注解step3: 使用注解4.1 Demo1: 字段值校验需求描述检测用户输入的 person 对象的 年龄值是否符合要求(0~200岁)、姓名的字符串长度是否符合要求(1 ~ 20个字符)解决方案通过注解的方式校验Person的年龄、name 是否符合要求。4.1 定义注解分析定义一个名称为Range的注解标记在需要校验的地方希望对 person对象的 年龄、姓名字段做校验所以注解使用目标Target 取字段类型ElementType.FIELD 要在程序运行时做校验Retention策略取 RetentionPolicy.RUNTIME校验字段是否属于某个范围(年龄的取值、姓名的字符长度)应有 最大值、最小值信息在 Range里定义两个参数方法minValue 和maxValueRetention(RetentionPolicy.RUNTIME)Target(ElementType.FIELD)publicinterfaceRange{intminValue()default0;intmaxValue()default250;}注解也是一种特殊的类使用interface关键字定义将自动继承java.lang.annotation.Annotation 接口编译程序将自动完成其他细节。一些需要注意的点在定义注解时不能继承其他的注解或接口。注解中的每一个方法实际上是声明了一个配置参数方法的名称就是参数的名称返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、Enum)。可以通过 default 来声明参数的默认值。4.2 处理注解根据需求可知自定义Range注解会用到两种类型的字段上数值型(年龄) 和 字符串型(姓名)在处理注解时需要分别针对这两种情况处理。可以写个专门处理 Range 注解的工具类方便在各处使用publicclassRangeCheckUtils{/** * 检查 Integer/String 类型的字段长度是否符合要求 * param object * throws IllegalArgumentException * throws ReflectiveOperationException */publicstaticvoidcheckFiledRange(Objectobject)throwsIllegalArgumentException,ReflectiveOperationException{//遍历传入对象所有的Filedfor(Fieldfield:object.getClass().getDeclaredFields()){//通过字段获取该字段上的注解 RangeRangerangefield.getAnnotation(Range.class);if(null!range){//获取注解 Range 定义的范围值intminValuerange.minValue();intmaxValuerange.maxValue();//获取 filed 的值field.setAccessible(true);ObjectfieldValuefield.get(object);// 按类型检查传入对象的 String 或 Integer 类型字段的范围是否符合要求if(fieldValueinstanceofString){StringstringValue(String)fieldValue;//字符型类型检查字符串长度if(stringValue.length()minValue||stringValue.length()maxValue){thrownewIllegalArgumentException(The length of a string filed is invalid, invalid filed: field.getName());}}if(fieldValueinstanceofInteger){intintegerValue(Integer)fieldValue;//数字型类型检查数字大小if(integerValueminValue||integerValuemaxValue){thrownewIllegalArgumentException(The value of a integer filed is invalid, invalid filed: field.getName());}}}}}}4.3 使用注解在Person类上使用注解publicclassPerson{/** * 姓名的 字符串长度范围为 120 个字符 * 这里使用注解设置name的长度范围 */Range(minValue1,maxValue20)privateStringname;/** * 年龄的范围是 0 200 * 这里使用注解设置age的大小范围 * 注解Range 的 minValue 0 故可以省去 */Range(maxValue200)privateIntegerage;/** * 性别不需要做检验 */privateStringgender;//get/set方法}测试注解功能功能是否符合预期测试代码如下publicclassTestAnnotation{publicstaticvoidmain(String[]args){//case1: name 和 age 都符合要求的person对象try{Personperson1newPerson();person1.setAge(20);person1.setName(BigBear);person1.setGender(male);//检查person的字段是否符合要求RangeCheckUtils.checkFiledRange(person1);System.out.println(case1 check pass ! person1 info: person1.toString());}catch(Exceptione){System.out.println(case1 check failed! exception info: e.getMessage());}//case2: name不符合要求的person对象try{Personperson2newPerson();person2.setAge(20);person2.setName(BigBearBigBearBigBearBigBearBigBear);person2.setGender(male);//检查person的字段是否符合要求RangeCheckUtils.checkFiledRange(person2);System.out.println(case2 check pass ! person1 info: person2.toString());}catch(Exceptione){System.out.println(case2 check failed! exception info: e.getMessage());}//case3: age不符合要求的person对象try{Personperson3newPerson();person3.setAge(270);person3.setName(BigBear);person3.setGender(male);//检查person的字段是否符合要求RangeCheckUtils.checkFiledRange(person3);System.out.println(case3 check pass ! person1 info: person3.toString());}catch(Exceptione){System.out.println(case3 check failed! exception info: e.getMessage());}//case4: age 和 name 都不符合要求的person对象try{Personperson4newPerson();person4.setAge(-1);person4.setName(BigBearBigBearBigBearBigBearBigBear);person4.setGender(male);//检查字段是否符合要求RangeCheckUtils.checkFiledRange(person4);System.out.println(case4 check pass ! person1 info: person4.toString());}catch(Exceptione){System.out.println(case4 check failed! exception info: e.getMessage());}}}对应的输出结果case1 check pass!person1 info:Person{nameBigBear,age20,gendermale}case2 check failed!exception info:Thelength of a string filed is invalid,invalid filed:name case3 check failed!exception info:Thevalue of a integer filed is invalid,invalid filed:age case4 check failed!exception info:Thelength of a string filed is invalid,invalid filed:name可见 RangeCheckUtils.checkFiledRange(person) 工具方法成功的通过注解找到了需要检查的 person 对象字段并检查出是否符合预期。4.2 Demo2: 自定义注解AOPAOP自定义注解的用法参考https://blog.csdn.net/taotiezhengfeng/article/details/149515395

相关新闻

Web前端之实现酷炫的仪表进度条、JavaScript动态设置Css属性值、标签元素属性选择器、hsl样式函数

Web前端之实现酷炫的仪表进度条、JavaScript动态设置Css属性值、标签元素属性选择器、hsl样式函数

MENU效果图HtmlJavaScriptStyle解析效果图 Html <div class"main"></div> <input type"range" id"range" value0> <p id"pc">0%</p>JavaScript function init() {let main document.querySelector(.…

2026/5/17 8:52:31 阅读更多 →
Web前端之UniApp、Taro、ReactNative和Flutter的区别

Web前端之UniApp、Taro、ReactNative和Flutter的区别

MENU前言介绍及公司技术差异使用方法使用场景差异注意事项打包与部署差异框架应用实例结语前言 在移动应用开发领域&#xff0c;跨平台框架已成为开发者的得力工具。UniApp、Taro、ReactNative和Flutter它们在Android&#xff08;安卓&#xff09;或iOS&#xff08;苹果&#x…

2026/5/17 8:52:30 阅读更多 →
Webpack5.6.0解决报The ‘mode‘ option has not been set, webpack will fallback to ‘production‘ fo的错误、问题

Webpack5.6.0解决报The ‘mode‘ option has not been set, webpack will fallback to ‘production‘ fo的错误、问题

MENU1、打包报错信息2、解决方案2.1、设置package.json2.2、设置webpack.config.js3、相关文章1、打包报错信息 // WARNING in configuration // The mode option has not been set, webpack will fallback to production // for thisvalue. Set mode option to development o…

2026/5/17 8:52:29 阅读更多 →

最新新闻

LTC6904与PIC24FJ256GB110构建高精度方波发生器方案

LTC6904与PIC24FJ256GB110构建高精度方波发生器方案

1. 项目背景与核心价值在嵌入式系统开发中&#xff0c;精确的时钟信号就像交响乐团的指挥家——它决定了整个系统的节奏和协调性。LTC6904这颗来自ADI的低功耗可编程振荡器芯片&#xff0c;配合PIC24FJ256GB110微控制器的强大处理能力&#xff0c;可以构建出频率精度达0.5%的方…

2026/7/7 6:45:50 阅读更多 →
许可证增购申请总被卡,管理层最想先看哪几项数据

许可证增购申请总被卡,管理层最想先看哪几项数据

摘要如果企业在没有完成使用分析的前提下就直接增购&#xff0c;往往会出现预算增加但利用率依旧偏低的情况。本文从高峰并发、模块结构、低效占用和历史趋势四个维度&#xff0c;分析为什么多数企业更适合先优化&#xff0c;再判断是否需要增购。 很多企业在 CAD、CAE、EDA 等…

2026/7/7 6:45:50 阅读更多 →
摩托车混流装配防错引导系统硬件怎么选?海雅达 Model 10X 工业三防平板电脑采购预算与部署成本评估

摩托车混流装配防错引导系统硬件怎么选?海雅达 Model 10X 工业三防平板电脑采购预算与部署成本评估

1. 摩托车混流装配的核心业务痛点与防呆控制难度在现代摩托车制造中&#xff0c;同一条总装流水线上通常处于跨骑、踏板、越野、纯电等多车型、多排量&#xff08;125cc 至 800cc&#xff09;的高度混流装配状态。一线操作工需要根据多变的车型工艺 BOM&#xff0c;装配不同的卡…

2026/7/7 6:45:50 阅读更多 →
ORB-SLAM3 SearchByBoW 2

ORB-SLAM3 SearchByBoW 2

以下是ORB-SLAM3中ORBmatcher::SearchByBoW函数的完整代码及详细解读。&#x1f4c4; 完整代码cppint ORBmatcher::SearchByBoW(KeyFrame* pKF, Frame &F, vector<MapPoint*> &vpMapPointMatches) {// 1. 获取关键帧的地图点const vector<MapPoint*> vpMap…

2026/7/7 6:43:49 阅读更多 →
F28377D Modbus RTU移植:硬件时序、寄存器映射与实时性硬核实践

F28377D Modbus RTU移植:硬件时序、寄存器映射与实时性硬核实践

1. 项目概述&#xff1a;为什么“半小时搞定”在嵌入式领域是个危险信号“跨平台移植modbus&#xff0c;从STM32到TMS320F28377D&#xff0c;kimi 2.6半小时搞定”——这个标题在嵌入式工程师朋友圈刷屏时&#xff0c;我正蹲在产线调试一台F28377D驱动的伺服控制器。第一反应不…

2026/7/7 6:39:48 阅读更多 →
BurpSuite乱码全解析:从编码原理到实战解决content-Encoding显示问题

BurpSuite乱码全解析:从编码原理到实战解决content-Encoding显示问题

1. 项目概述&#xff1a;当BurpSuite遇上乱码如果你是一名Web安全测试人员或者开发者&#xff0c;那么BurpSuite这个工具对你来说肯定不陌生。它几乎是渗透测试、接口调试和Web应用分析领域的“瑞士军刀”。但在日常使用中&#xff0c;一个看似简单却极其恼人的问题频繁出现&am…

2026/7/7 6:37:48 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集&#xff0c;从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高&#xff1a;流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广&#xff0c;是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队&#xff0c;往往容易卡在一容易踩坑的选型难题&#xff1a; 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定&#xff0c;必须同时依赖「文本语义理解 实体关系推理」时&#xff0c;到底是做「向量图谱混合检索」就够了&#xff0c;还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools&#xff1a;5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里&#xff0c;参与了关于混合后量子密码学的讨论&#xff0c;应付端点攻击找茬的人&#xff0c;还参与留言板讨论后&#xff0c;发现“威胁模型”对多数人仍是陌生概念&#xff0c;且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”&#xff1a;我理解的渗透测试到底是什么&#xff1f;每次看到新闻里说某个大公司的数据被“黑”了&#xff0c;或者某个网站被攻击导致服务瘫痪&#xff0c;你是不是和我一样&#xff0c;心里会冒出两个念头&#xff1a;一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻