首页 > Java > java教程 > 正文

在 Spring Boot 中创建自定义注释的终极指南

PHPz
发布: 2024-08-25 18:01:02
原创
657 人浏览过

The Ultimate Guide to Create Custom Annotations in Spring Boot
这样的注解充满了 Spring Boot 中的整个项目。

但是你知道这些注解解决了什么问题吗?

为什么要引入自定义注释?

如何创建自定义注释?

今天,我将介绍:

  • 为什么要创建自定义注释?
  • 使用这些注释的主要好处是什么?
  • 如何创建自定义注释?
  • 带注解的方法是如何被调用的?
  • 何时使用自定义注释?
  • 什么时候不应该使用自定义注释?
  • 使用自定义注释有哪些缺点?

?为什么要创建自定义注释?

在 Spring Boot 中,注释不仅仅是添加元数据的一种方式。他们

  • 简化复杂的任务
  • 减少样板代码
  • 增强代码可读性

在 Spring 引入自定义注释之前,开发人员必须使用 XML 配置文件来管理电子邮件验证等配置。

XML 配置将定义 bean、验证器和其他必要的组件来执行验证电子邮件地址等任务。

以下是如何在 Spring 应用程序中使用 XML 配置电子邮件验证的示例:

The Ultimate Guide to Create Custom Annotations in Spring Boot

如您所见,这很容易成为一场噩梦,因为有数百个类,其中许多类相互依赖。

这也意味着开发人员每次必须添加新的依赖项时都必须查找此 XML。

自定义注释的主要优点

简化配置

Spring 引入了自定义注释来简化配置,允许开发人员直接在代码中使用注释。

这减少了对大量 XML 配置的需求,使代码库更干净且更易于维护。

支持声明式编程

Spring 中的自定义注释启用了声明式方法。

开发者可以使用 @Transactional、@Cacheable 或 @Scheduled 等注解来声明所需的行为,而无需编写底层逻辑。

这会产生更具可读性和可维护性的代码。

处理跨领域问题

Spring 的自定义注解通常与面向方面编程(AOP)一起使用,允许开发人员以集中的方式处理横切关注点。

例如,@Transactional 注解可以跨多个方法或类管理事务,而无需将事务管理逻辑分散在整个代码中。

减少样板代码

它通过封装常见行为减少了对样板代码的需求。

例如,@Autowired 注解简化了依赖注入,允许 Spring 自动注入依赖项,而不需要显式的构造函数或 setter 方法

是否应该使用@Autowired 是一个不同的讨论。

提高代码可读性和一致性

通过将配置和横切关注点抽象为注解,Spring 提高了代码的可读性。

您和您的同行开发人员可以通过查看方法或类的注释来快速了解方法或类的用途,并且注释有助于增强整个代码库的一致性。

框架灵活性和可扩展性

自定义注释允许开发人员创建适合特定需求的注释,从而以标准化的方式扩展框架的功能。

这种灵活性帮助 Spring 在多个应用程序和架构中保持相关性和强大功能。

?如何创建自定义注释

第 1 步:定义注释

  • 通过定义接口创建新的注释。
  • 使用@interface来声明它。
  • 添加元注释以指定注释的行为方式。
package co.officegeek.tokenratelimiter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)  // Annotation available at runtime
@Target(ElementType.METHOD)          // Can be applied to methods
public @interface LogExecutionTime {
}
登录后复制
  • @Target:指示可以使用注解的位置(例如方法、类)。
  • @Retention:指示注释保留多长时间(例如,运行时、编译时)。

步骤 2:创建一个方面来处理注释

您可以使用 Spring 的 BeanPostProcessor、Aspect 或自定义注解处理逻辑创建自定义逻辑来处理注解。

package co.officegeek.tokenratelimiter;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogExecutionTimeAspect {

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        return proceed;
    }
}
登录后复制

第 3 步:应用注释

将自定义注释应用到定义的方法、字段或类。

package co.officegeek.tokenratelimiter;

import org.springframework.stereotype.Service;

@Service
public class TestService {

    @LogExecutionTime
    public void serve() throws InterruptedException {
        // Simulate some work
        Thread.sleep(2000);
    }
}
登录后复制

How It Works:

  • The @LogExecutionTime annotation doesn't cause any method to be called directly.
  • The Spring AOP framework detects that a method has the @LogExecutionTime annotation using reflection.
  • The LogExecutionTimeAspect aspect is configured to apply around advice when a method with the @LogExecutionTime annotation is called.
  • The logExecutionTime method in the aspect is executed before and after the annotated method (serve), logging the execution time.

The Ultimate Guide to Create Custom Annotations in Spring Boot


How does the annotated method get invoked?

When you apply a custom annotation to a method, class, or field, the annotation itself doesn't directly cause any method to be called.

Instead, the logic associated with the annotation is typically implemented using reflection or aspect-oriented programming (AOP) in frameworks like Spring.

Here's a breakdown of how the compiler and runtime environment know what method to call when an annotation is applied:

1. Compile-Time Processing (Annotation Processors)

Some annotations are handled at compile time by annotation processors.

Java's javax.annotation.processing package allows developers to create custom annotation processors that generate code, validate annotations, or even modify the abstract syntax tree (AST) of the code being compiled.

The annotation processor reads the annotations during compilation and executes code based on those annotations.

This can include generating new classes or methods that the code will use later.

The @Override annotation is a compile-time annotation that doesn't invoke a method but instead tells the compiler to check if the method actually overrides a superclass method.

How It Works:

  • You define a custom annotation processor by extending AbstractProcessor and overriding the process method.
  • The processor will be invoked by the compiler when it encounters your annotation, allowing you to generate code or perform other tasks.

2. Runtime Processing (Reflection)

Custom annotations can be processed at runtime using reflection.

The runtime system (e.g., a framework like Spring) uses reflection to detect the presence of annotations on methods, classes, or fields, and then applies the corresponding behavior.

A custom annotation like @LogExecutionTime doesn't directly trigger any method call.

Instead, an aspect or some other reflective mechanism checks for the presence of the annotation at runtime and then wraps the method call with additional logic.

How It Works:

  • At runtime, you use Java's reflection API to check if a method or class has a specific annotation using methods like isAnnotationPresent.
  • Once detected, you can invoke methods or execute logic associated with that annotation.  For example, if a method has a @LogExecutionTime annotation, you might measure the time before and after the method call.

3. Aspect-Oriented Programming (AOP)

In frameworks like Spring, AOP is commonly used to handle custom annotations.

AOP allows you to define "aspects" that can intercept method calls and perform additional processing before or after the method execution.

When the AOP framework (e.g. Spring AOP) detects an annotation, it triggers the execution of an advice method associated with the aspect.

This advice method contains the logic that the AOP framework executes when the annotated method is called.

A @Transactional annotation in Spring doesn't execute any logic by itself.

Instead, the Spring framework's AOP infrastructure intercepts calls to methods annotated with @Transactional and wraps them with transaction management logic.

How It Works:

  • You define an aspect class with advice methods that are associated with specific pointcuts (join points where you want to apply the advice).
  • The aspect uses annotations like @Around or @Before to specify when the advice should be executed.
  • The AOP framework ensures that when a method with a custom annotation is called, the corresponding advice is executed automatically.

Use Cases Where Custom Annotations Are a Good Approach

Cross-Cutting Concerns

Custom annotations are ideal for handling cross-cutting concerns like logging, security, transaction management, and caching.

These are concerns that affect multiple parts of an application but are not related to the core business logic.

上面的@LogExecutionTime注释是一个很好的例子,因为它可以在所有方法中使用,并且它没有任何业务逻辑。

声明式编程

当您想要指定应该发生什么而不是如何发生时,自定义注释提供了一种干净且富有表现力的方式来执行此操作。

@Cacheable 或 @Retry 等注解允许开发人员以声明方式启用缓存或重试逻辑,而无需手动编写实现代码。

框架或库集成

自定义注释可以通过隐藏易于使用的注释背后的复杂性来简化框架或库的集成。

Spring 中的 @Autowired 这样的注释有助于注入依赖项,而无需手动实例化它们。

复杂逻辑的封装

当需要以可重用的方式封装复杂逻辑时,自定义注释可以提供一个干净的 API 来应用此逻辑。

像@RateLimit这样的注解可以封装逻辑来限制方法被调用的次数,而不会用这个逻辑扰乱方法的主体。

不应使用自定义注释的用例

简单或一次性的逻辑

如果逻辑很简单或者只需要在单个位置应用,那么创建自定义注释就太过分了,并且会使代码不必要地复杂化。

需要动态行为的逻辑

注解是在编译时静态定义的,不适合需要在运行时动态确定行为的场景。

如果方法的行为应根据用户输入或外部配置而改变,则使用自定义注释处理此问题可能会导致复杂的解决方案。

业务逻辑

核心业务逻辑不应抽象为自定义注释,因为这会使逻辑不太透明且难以维护。

使用@ProcessOrder这样的注解来封装业务流程可能会隐藏重要的业务规则,使代码更难理解和维护。

注释之间复杂的交互

如果行为依赖于多个注释之间的复杂交互,则可能会导致意外结果并使代码难以理解和调试。

组合影响同一方法的多个自定义注释(例如@Retry、@Cacheable、@LogExecutionTime)可能会导致不可预测的行为并且难以管理

性能关键代码

自定义注释通常依赖于反射或代理机制,这可能会带来性能开销。

它们不应该用在代码的性能关键部分。

使用自定义注释向在紧密循环中调用数百万次的方法添加日志记录可能会显着降低性能。

?摘要 - 何时使用自定义注释

自定义注释非常适合处理横切问题,例如日志记录、安全性和事务管理。

它们也非常适合您需要在应用程序的多个部分应用相同行为的场景。

但是,对于简单、一次性的逻辑,或者需要细粒度控制和灵活性的情况,自定义注释可能不是最好的方法。

在决定实施之前考虑权衡。

?最后的想法

自定义注释是 Spring Boot 武器库中的一个强大工具,但像任何工具一样,应该谨慎使用它们。

它们提供了一种干净、可重用的方式来处理重复性任务并强制整个代码库的一致性。

但请注意潜在的缺点,尤其是复杂性和性能方面。


??公告

我正在为软件开发人员和有抱负的微服务架构师推出为期 10 天的队列课程,介绍如何使用 Spring Boot 和 Bucket4j 设计和实现速率限制服务。

您将学到:

✅ 如何设计和构建可投入生产的微服务

✅ 深入了解速率限制算法及其实现

✅ Spring Boot 开发、测试和容器化的最佳实践

但这也是关于

✅ 将项目分解为具体任务

✅ 对自己负责

✅ 正确设计和构建项目

它针对的是想要设计和开发微服务的软件开发人员,这是与大多数公司相关的用例。

特别适合那些处于软件开发人员职业生涯早期的人,他们可能没有“项目经验”,但拥有大量的热情和雄心。

如果您认为这对您有帮助,或者即使您只是想了解更多:

登记您的兴趣,我会让您知道研讨会的详细信息。


这首先发布在我的子堆栈上。订阅我的 Substack - Weekend Developer 以第一时间获取更新。

您是需要对您编写的代码提供反馈的开发人员吗?

或者您希望有人审查您的代码以便您做正确的事情?

我帮助人们进行免费的代码审查会议,以便他们能够尽早获得反馈并编写更好的代码

在 Twitter (X) 或 LinkedIn 上私信我,我将帮助您编写代码。

以上是在 Spring Boot 中创建自定义注释的终极指南的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板