Such annotations fill the entire project in Spring Boot.
But do you know what problems these annotations solve?
Why were custom annotations introduced to begin with?
How to create custom annotations?
Today, I will cover:
In Spring Boot, annotations are more than just a way to add metadata. They
Before Spring introduced custom annotations, developers had to manage configurations like email validation using XML configuration files.
The XML configuration would define beans, validators, and other necessary components to perform tasks such as validating email addresses.
Here's an example of how email validation might have been configured using XML in a Spring application:
As you can see, this can easily become a nightmare where there are hundreds of classes with many of them relying on each other.
It also meant a developer had to go look up this XML every time they had to add a new dependency.
Spring introduced custom annotations to simplify configuration by allowing developers to use annotations directly in their code.
This reduced the need for extensive XML configuration, making the codebase cleaner and easier to maintain.
Custom annotations in Spring enable a declarative approach.
Developers can use annotations like @Transactional, @Cacheable, or @Scheduled to declare desired behaviors without writing the underlying logic.
This results in more readable and maintainable code.
Spring's custom annotations, often used with Aspect-Oriented Programming (AOP), allow developers to handle cross-cutting concerns in a centralized manner.
For example, the @Transactional annotation manages transactions across multiple methods or classes without scattering transaction management logic throughout the code.
It reduces the need for boilerplate code by encapsulating common behaviours.
For instance, the @Autowired annotation simplifies dependency injection, allowing Spring to automatically inject dependencies, rather than requiring explicit constructor or setter methods
It is a different discussion whether you should be using @Autowired or not.
By abstracting configuration and cross-cutting concerns into annotations, Spring improves the readability of the code.
You and your peer developers can quickly understand the purpose of a method or class by looking at its annotations, and annotations help enforce consistency across the codebase.
Custom annotations allow developers to create their annotations tailored to specific needs, thus extending the framework's functionality in a standardized way.
This flexibility has helped Spring remain relevant and powerful across multiple applications and architectures.
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 { }
You can create a custom logic to process the annotation using Spring's BeanPostProcessor, Aspect, or custom annotation processing logic.
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; } }
Apply your custom annotation to methods, fields, or classes as defined.
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); } }
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:
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.
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.
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.
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 무기고의 강력한 도구이지만 다른 도구와 마찬가지로 신중하게 사용해야 합니다.
반복적인 작업을 처리하고 코드베이스 전체에 일관성을 강화할 수 있는 깔끔하고 재사용 가능한 방법을 제공합니다.
그러나 특히 복잡성과 성능의 경우 잠재적인 단점에 유의하세요.
저는 소프트웨어 개발자와 주목받는 마이크로서비스 설계자를 대상으로 Spring Boot 및 Bucket4j를 사용하여 속도 제한 서비스를 설계하고 구현하는 방법에 대한 10일 코호트 기반 과정을 시작합니다.
배우게 될 내용:
✅ 프로덕션에 바로 사용할 수 있는 마이크로서비스를 설계하고 구축하는 방법
✅ 속도 제한 알고리즘 및 구현에 대한 심층적인 지식
✅ Spring Boot 개발, 테스트 및 컨테이너화 모범 사례
하지만 그것도
✅ 프로젝트를 특정 작업으로 나누기
✅ 자신에게 책임을 지기
✅ 프로젝트를 올바르게 설계하고 구축하기
대부분의 회사와 관련된 사용 사례인 마이크로서비스를 설계하고 개발하려는 소프트웨어 개발자를 대상으로 합니다.
특히 "프로젝트 경험"은 없지만 열정과 야망이 넘치는 소프트웨어 개발자 경력 초기의 사람들을 위한 것입니다.
이 내용이 도움이 될 것이라고 생각하거나 더 알고 싶은 경우:
관심등록하시면 워크숍 세부사항을 알려드리겠습니다.
이 내용은 내 서브스택에 처음 게시되었습니다. 업데이트를 먼저 받으려면 내 Substack - Weekend Developer를 구독하세요.
당신이 작성한 코드에 대한 피드백이 필요한 개발자이신가요?
아니면 당신이 올바른 일을 할 수 있도록 누군가가 당신의 코드를 검토하길 원하시나요?
저는 사람들이 조기에 피드백을 받고 더 나은 코드를 작성할 수 있도록 무료 코드 검토 세션을 제공합니다
Twitter(X) 또는 LinkedIn에서 DM을 보내주시면 코드 작성에 도움을 드리겠습니다.
The above is the detailed content of The Ultimate Guide to Create Custom Annotations in Spring Boot. For more information, please follow other related articles on the PHP Chinese website!