你的开发利器Spring自定义注解

简介: 自定义注解在开发中是一把利器,经常会被使用到。在上一篇文章中有提到了自定义校验注解的用法。 然而最近接到这样一个需求,主要是针对某些接口的返回数据需要进行一个加密操作。于是很自然的就想到了自定义注解+AOP去实现这样一个功能。但是对于自定义注解,只是停留在表面的使用,没有做到知其然,而知其所以然。所以这篇文章就是来了解自定义注解这把开发利器的。

前言

  自定义注解在开发中是一把利器,经常会被使用到。在上一篇文章中有提到了自定义校验注解的用法。 然而最近接到这样一个需求,主要是针对某些接口的返回数据需要进行一个加密操作。于是很自然的就想到了自定义注解+AOP去实现这样一个功能。但是对于自定义注解,只是停留在表面的使用,没有做到知其然,而知其所以然。所以这篇文章就是来了解自定义注解这把开发利器的。

什么是自定义注解?

官方定义

  An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

Google翻译一下

  注解是元数据的一种形式,可以添加到Java源代码中。 类,方法,变量,参数和包都可以被注释。 注解对其注释的代码的操作没有直接影响。

看完这个定义是不是有点摸不到头脑,不要慌实践出真知。

建立一个自定义注解

  我们先回顾一下需求的场景,是要针对xx接口的返回数据需要做一个加密操作。之前说到使用自定义注解+AOP来实现这个功能。所以我们先定义一个注解叫Encryption,被Encryption注解修饰后接口,返回的数据要被加密。

public @interface Encryption {
   
   
}

  你会发现创建自定义注解,就和建立普通的接口一样简单。只是所使用的关键字有所不同。在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。

编写相应的接口

@Encryption
@GetMapping("/encrypt")
public ResultVo encrypt(){
   
   
    return ResultVoUtil.success("不一样的科技宅");
}

@GetMapping("/normal")
public ResultVo normal(){
   
   
    return ResultVoUtil.success("不一样的科技宅");
}

编写切面

@Around("@annotation(com.hxh.unified.param.check.annotation.Encryption)")
public ResultVo encryptPoint(ProceedingJoinPoint joinPoint) throws Throwable {
   
   
  ResultVo resultVo = (ResultVo) joinPoint.proceed();

  // 获取注解
  MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  Method method = methodSignature.getMethod();
  Encryption annotation = method.getAnnotation(Encryption.class);

  // 如果被标识了,则进行加密
  if(annotation != null){
   
   
    // 进行加密
    String encrypt = EncryptUtil.encryptByAes(JSON.toJSONString(resultVo.getData()));
    resultVo.setData(encrypt);
  }

  return resultVo;
}

测试结果

  这个时候,你会发现返回的数据并没有被加密。 那么这个是为啥呢?俗话说遇到问题不要慌,先掏出手机发个朋友圈(稍微有点跑题了)。出现这个原因是,缺少了@Retention@Encryption的修饰,让我们把它加上。

@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {
   
   

}

继续测试

  这个时候返回的数据就被加密了,说明自定义注解生效了。

测试普通接口

  没有用@Encryption的接口,返回的数据没有被加密。到此需求就已经实现了,接下来就该了解原理了。

@Retention

@Retention作用是什么

  Retention的翻译过来就是"保留"的意思。也就意味着它的作用是,用来定义注解的生命周期的,并且在使用时需要指定RetentionPolicyRetentionPolicy有三种策略,分别是:

  • SOURCE - 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃。
  • CLASS - 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。
  • RUNTIME - 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

选择合适的生命周期

  首先要明确生命周期 RUNTIME > CLASS > SOURCE 。一般如果需要在运行时去动态获取注解信息,只能使用RUNTIME。如果要在编译时进行一些预处理操作,比如生成一些辅助代码就用CLASS。如果只是做一些检查性的操作,比如 @Override和@SuppressWarnings,则可选用 SOURCE。

我们实际开发中的自定义注解几乎都是使用的RUNTIME

  最开始@Encryption没有使用@Retention对其生命周期进行定义。所以导致AOP在获取的时候一直为空,如果为空就不会对数据进行加密。

  是不是感觉这个注解太简陋。那再给他加点东西,加上个@Target

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {
   
   

}

@Target

  @Target注解是限定自定义注解可以使用在哪些地方。这就和参数校验一样,约定好规则,防止乱用而导致问题的出现。针对上述的需求可以限定它只能用方法上。根据不同的场景,还可以使用在更多的地方。比如说属性、包、构造器上等等。

  • TYPE - 类,接口(包括注解类型)或枚举
  • FIELD - 字段(包括枚举常量)
  • METHOD - 方法
  • PARAMETER - 参数
  • CONSTRUCTOR - 构造函数
  • LOCAL_VARIABLE - 局部变量
  • ANNOTATION_TYPE -注解类型
  • PACKAGE - 包
  • TYPE_PARAMETER - 类型参数
  • TYPE_USE - 使用类型

  上面两个是比较常用的元注解,Java一共提供了4个元注解。你可能会问元注解是什么?元注解的作用就是负责注解其他注解。

@Documented

  @Documented的作用是对自定义注解进行标注,如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来。没什么实际作用,了解一下就好了。

@Inherited

  被@Inherited修饰的注解,被用在父类上时其子类也拥有该注解。 简单的说就是,当在父类使用了被@Inherited修饰的注解@InheritedTest时,继承它的子类也拥有@InheritedTest注解。

这个可以单独讲下

注解元素类型

  参照我们在定义接口的经验,在接口中能定义方法和常量。但是在自定义注解中,只能定义一个东西:注解类型元素Annotation type element

其实可以简单的理解为只能定义方法,但是和接口中的方法有区别。

定义注解类型元素时需要注意如下几点:

  • 访问修饰符必须为public,不写默认为public。
  • 元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型。
  • type()括号中不能定义方法参数,仅仅只是一个特殊的语法。但是可以通过default关键字设置"默认值"。
  • 如果没有默认值,则使用注解时必须给该类型元素赋值。

继续改造

  需求这个东西经常都在变动。原本需要加密的接口只使用AES进行加密,后面又告知有些接口要使用DES加密。针对这样的情况,我们可以在注解内,添加一下配置项,来选择使用何种方式加密。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {
   
   

    /**
     * 加密类型
     */
    String value() default "AES";

}

调整接口

@Encryption
@GetMapping("/encrypt")
public ResultVo encrypt(){
   
   
    return ResultVoUtil.success("不一样的科技宅");
}

@Encryption(value = "DES")
@GetMapping("/encryptDes")
public ResultVo encryptDes(){
   
   
    return ResultVoUtil.success("不一样的科技宅");
}

@GetMapping("/normal")
public ResultVo normal(){
   
   
    return ResultVoUtil.success("不一样的科技宅");
}

调整AOP

@Around("@annotation(com.hxh.unified.param.check.annotation.Encryption)")
public ResultVo encryptPoint(ProceedingJoinPoint joinPoint) throws Throwable {
   
   
  ResultVo resultVo = (ResultVo) joinPoint.proceed();

  // 获取注解
  MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  Method method = methodSignature.getMethod();
  Encryption annotation = method.getAnnotation(Encryption.class);

  // 如果被标识了,则进行加密
  if(annotation != null){
   
   
    // 进行加密
    String encrypt = null;
    switch (annotation.value()){
   
   
      case "AES":
        encrypt = EncryptUtil.encryptByAes(JSON.toJSONString(resultVo.getData()));
        break;
      case "DES":
        encrypt = EncryptUtil.encryptByDes(JSON.toJSONString(resultVo.getData()));
        break;
      default:
        break;
    }
    resultVo.setData(encrypt);
  }

  return resultVo;
}

  至此就改造完了。可以发现注解元素类型,在使用的时候,操作元素类型像在操作属性。解析的时候,操作元素类型像在操作方法。

小技巧

  • 当注解没有注解类型元素,使用时候可直接写为@Encryption@Encryption()等效。
  • 当注解只有一个注解类型元素,并且命名是value。在使用时@Encryption("DES")@Encryption(value = "DES")等效。

注意的点

  • 需要根据实际情况指定注解的生命周期@Retention
  • 使用@Target来限制注解的使用范围,防止注解被乱用。
  • 如果注解是配置在方法上的,那么我们要从Method对象上获取。如果是配置在属性上,就需要从该属性对应的Field对象上去获取。总之用在哪里,就去哪里获取。

总结

  注解可以理解为就是一个标识。可以在程序代码中的关键节点上打上这些标识,它不会改变原有代码的执行逻辑。然后程序在编译时或运行时可以检测到这些标记,在做出相应的操作。结合上面的小场景,可以得出自定义注解使用的基本流程:

  1. 定义注解 --> 根据业务进行创建。
  2. 使用注解 --> 在相应的代码中进行使用。
  3. 解析注解 --> 在编译期或运行时检测到标记,并进行特殊操作。

结尾

  如果觉得对你有帮助,可以多多评论,多多点赞哦,也可以到我的主页看看,说不定有你喜欢的文章,也可以随手点个关注哦,谢谢。

  我是不一样的科技宅,每天进步一点点,体验不一样的生活。我们下期见!

相关文章
|
3月前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
4287 66
|
4月前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
670 128
|
3月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
3月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
468 2
|
4月前
|
Java 测试技术 数据库
使用Spring的@Retryable注解进行自动重试
在现代软件开发中,容错性和弹性至关重要。Spring框架提供的`@Retryable`注解为处理瞬时故障提供了一种声明式、可配置的重试机制,使开发者能够以简洁的方式增强应用的自我恢复能力。本文深入解析了`@Retryable`的使用方法及其参数配置,并结合`@Recover`实现失败回退策略,帮助构建更健壮、可靠的应用程序。
536 1
使用Spring的@Retryable注解进行自动重试
|
4月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
351 12
|
4月前
|
监控 安全 Java
使用 @HealthEndpoint 在 Spring Boot 中实现自定义健康检查
Spring Boot 通过 Actuator 模块提供了强大的健康检查功能,帮助开发者快速了解应用程序的运行状态。默认健康检查可检测数据库连接、依赖服务、资源可用性等,但在实际应用中,业务需求和依赖关系各不相同,因此需要实现自定义健康检查来更精确地监控关键组件。本文介绍了如何使用 @HealthEndpoint 注解及实现 HealthIndicator 接口来扩展 Spring Boot 的健康检查功能,从而提升系统的可观测性与稳定性。
319 0
使用 @HealthEndpoint 在 Spring Boot 中实现自定义健康检查
|
4月前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
218 0
探索Spring Boot的@Conditional注解的上下文配置
|
4月前
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
177 0
Spring中最大化@Lazy注解,实现资源高效利用
|
4月前
|
安全 数据可视化 Java
AiPy开发的 Spring 漏洞检测神器,未授权访问无所遁形
针对Spring站点未授权访问问题,现有工具难以检测如Swagger、Actuator等组件漏洞,且缺乏修复建议。全新AI工具基于Aipy开发,具备图形界面,支持一键扫描常见Spring组件,自动识别未授权访问风险,按漏洞类型标注并提供修复方案,扫描结果可视化展示,支持导出报告,大幅提升渗透测试与漏洞定位效率。