그러면 여기서 궁금할꺼다. getMergedAnnotationAttributes의 spring framework에서의 역할이다.
언재 사용하는걸까? 이것저것 찾아보니 어노테이션 커스텀하는경우 이용을 한다고 합니다.
예제로 간단히 코드로 보겠습니다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@BaseAnnotation("defaultBase")
public @interface CustomConfig {
// 운영 환경, 로깅 레벨, 캐시 지속시간 등 필요한 옵션 추가
String env() default "prod";
int cacheDuration() default 300;
}
@Component
public class BusinessService {
public void performBusinessLogic() {
// 현재 클래스 및 메소드에 대한 병합된 애너테이션 속성 조회
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(this.getClass(), CustomConfig.class);
if (attributes != null) {
String env = (String) attributes.get("env");
int cacheDuration = (Integer) attributes.get("cacheDuration");
// env에 따른 로직 분기 또는 캐싱 로직 초기화
System.out.println("Running in environment: " + env + ", Cache duration: " + cacheDuration);
}
// 실제 비즈니스 로직 실행
}
}
@Test
void getMergedAnnotationAttributesWithConventionBasedComposedAnnotation() {
Class<?> element = ConventionBasedComposedContextConfigClass.class;
String name = ContextConfig.class.getName();
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
assertThat(attributes).as("Should find @ContextConfig on " + element.getSimpleName()).isNotNull();
// Convention-based annotation attribute overrides are no longer supported as of
// Spring Framework 7.0. Otherwise, we would expect "explicitDeclaration".
assertThat(attributes.getStringArray("locations")).as("locations").isEmpty();
assertThat(attributes.getStringArray("value")).as("value").isEmpty();
// Verify contracts between utility methods:
assertThat(isAnnotated(element, name)).isTrue();
}
ConventionBasedComposedContextConfigClass
해당 클래스를 확인해보면
해당 클래스를 location 속성에를 explicitDeclaration 선언한 상태입니다.
@ConventionBasedComposedContextConfig(locations = "explicitDeclaration")
static class ConventionBasedComposedContextConfigClass {
}
locations 속성은 어노테이션을 적용할 때, 관련된 구성 정보(예를 들어, 애플리케이션 컨텍스트 설정 파일이나 리소스)의 위치를 지정하기 위한 문자열 배열입니다.
spring -core 코드를 보면 해당 어노테이션에 locations = "explicitDeclaration" 가 존재합니다.
@ConventionBasedComposedContextConfig(locations = "explicitDeclaration")
static class ConventionBasedComposedContextConfigClass {
}
마커(marker): 자바에서는 마커 인터페이스처럼, 아무런 메서드도 포함하지 않고 단순히 어떤 클래스나 요소가 특정 특성을 가진다는 것을 표시하기 위해 사용되는 인터페이스나 어노테이션을 말합니다. 예를 들어, Serializable 인터페이스는 어떤 클래스가 직렬화 가능함을 나타내는 마커 역할을 합니다.
플래그(flag): 플래그는 코드 내에서 어떤 조건이나 상태를 나타내기 위해 사용되는 신호(일종의 상수 값)입니다. 어떤 동작을 제어하거나 구분할 때 "이 값이 설정되었으면, ~한 동작을 수행하라"와 같이 조건문에서 활용할 수 있습니다.
여기서 explicitDeclaration는 명시적 선언을 한 상황입니다.
해당 인터페스이스를 보면
@ContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface ConventionBasedComposedContextConfig {
// Do NOT use @AliasFor here
String[] locations() default {};
}
@ContextConfig 이 어노테이션은 Spring의 컨텍스트 설정과 관련된 어노테이션입니다. 즉, ConventionBasedComposedContextConfig가 Spring 테스트나 애플리케이션 컨텍스트 구성에 관여함을 암시합니다.
@Retention(RetentionPolicy.RUNTIME) 이 어노테이션은 런타임까지 유지되어, 실행 중 리플렉션(reflection)을 통해 해당 어노테이션 정보를 조회할 수 있게 합니다.
어노테이션 선언 (@interface ConventionBasedComposedContextConfig) 여기서 새 어노테이션 타입인 ConventionBasedComposedContextConfig를 정의합니다.
이 코드는 Spring 컨텍스트 구성과 관련된 사용자 정의 어노테이션으로, locations라는 속성을 통해 구성 정보를 제공할 수 있도록 설계되었으며, 런타임에도 해당 정보가 유지되어 필요한 시점에 리플렉션으로 접근할 수 있도록 되어 있습니다.
스레드 개념을 한번쯤 보면 좋을것같습니다 ㅎ 둘은 좀 다르지만 일단 둘다 비동기 처리입니다.(기초가 중요하다!)
@Async 어노테이션은 주로 Spring Framework에서 사용되며, 메서드를 비동기적으로 실행할 수 있도록 해줍니다. 이를 통해 긴 작업이나 I/O 작업 등을 별도의 스레드에서 실행하여, 호출하는 쪽에서는 즉시 다음 작업을 진행할 수 있게 됩니다.
비동기 처리 활성화: @Async를 사용하기 전에, 애플리케이션 설정에 @EnableAsync를 추가하여 비동기 처리를 활성화해야 합니다.
@Configuration
@EnableAsync
public class AsyncConfig {
// Executor 설정 등 추가 설정 가능
}
메서드에 적용: 비동기 처리가 필요한 메서드 위에 @Async 어노테이션을 붙입니다. 이 메서드는 호출한 스레드와 별도로, 스레드 풀에서 실행됩니다.
@Service
public class MyService {
@Async
public void executeAsyncTask() {
// 시간이 오래 걸리는 작업
}
}
반환 타입:
void: 결과를 기다리지 않을 경우 사용합니다.
Future / CompletableFuture: 비동기 작업의 결과를 받아보고 싶을 때 사용합니다.
@Async
public CompletableFuture<String> asyncMethodWithReturn() {
// 처리 로직
return CompletableFuture.completedFuture("완료");
}
주의 사항:
public 메서드여야 프록시를 통한 AOP 적용이 제대로 이루어집니다.
같은 클래스 내부에서 호출하는 경우에는 프록시가 적용되지 않아 비동기 처리가 동작하지 않을 수 있으므로, 별도의 빈으로 분리하여 호출하는 것이 좋습니다.
스레드 풀 설정을 커스터마이징하여, 애플리케이션에 맞는 스레드 수와 동작 방식을 조정할 수 있습니다.
주의사항
스레드 관리: 비동기 처리 시에는 스레드 풀이나 이벤트 루프 등을 적절하게 설정해, 자원 누수나 과도한 스레드 생성 문제를 피해야 합니다.
동시성 이슈: 여러 스레드에서 동시에 데이터에 접근할 경우 동기화 문제가 발생할 수 있으므로, 상태 관리에 주의해야 합니다.
프레임워크 선택: Java에서는 Netty, Spring WebSocket, Reactor 등 비동기 처리를 지원하는 프레임워크를 활용하면 편리합니다.
간단히 알아봤는데요. 음 뭐 스레드 사용해서 비동기 처리하는 부분인데요. 이론은 알아도 개발하면서 이해하는 것 만큼 좋은것 없는것같습니다.
다음에 소켓통신을 통해서 개발한 부분을 비동기 처리를 해보도록 하겠습니다. ㅎㅎ ( 실은 회사에서 업무할때 자동화 시스템 추가 개발하는데 async를 써보는데 파일업로드가 비동기 처리가 안되더라고요 , 이유가 파일 업로드의 경우 클라이언트와의 연결이 파일 전송 완료까지 유지되어야 하고, 해당 요청 자체가 블로킹 I/O를 필요로 하므로, 비동기 처리의 효과가 제한이 되더라거요 ㅎ 사실 자주 안써보다보니 이번에 같이 공부하면서 자세히 다뤄보도록 하겠습니다. )
Builder 패턴은 복잡한 객체의 생성 과정을 단순화하고 가독성을 높이기 위해 사용되는 생성 패턴(Creational Pattern) 중 하나입니다. 특히 매개변수가 많은 클래스를 생성할 때, 생성자 또는 정적 팩토리 메서드보다 더 직관적이고 명확한 방법을 제공합니다.
2. Builder 패턴의 장점
가독성 향상: 어떤 필드가 어떤 값으로 설정되는지 명확하게 보입니다.
불변성 유지: 객체 생성 후 필드를 변경할 수 없게 하여 불변 객체를 쉽게 만들 수 있습니다.
유연한 객체 생성: 필수 매개변수와 선택 매개변수를 구분하여 유연하게 객체를 생성할 수 있습니다.
코드 중복 감소: 여러 생성자 오버로딩 없이 다양한 조합으로 객체를 생성할 수 있습니다.
3. 전통적인 Builder 패턴 예제
public class User {
private final String firstName;
private final String lastName;
private final int age;
private final String email;
// private 생성자
private User(Builder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.email = builder.email;
}
// static nested Builder 클래스
public static class Builder {
private String firstName;
private String lastName;
private int age;
private String email;
public Builder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder lastName(String lastName) {
this.lastName = lastName;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
// 최종적으로 객체를 생성하는 build 메서드
public User build() {
return new User(this);
}
}
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
// 객체 생성 예제
public class Main {
public static void main(String[] args) {
User user = new User.Builder()
.firstName("John")
.lastName("Doe")
.age(30)
.email("john.doe@example.com")
.build();
System.out.println(user);
}
}
Lombok은 자바에서 보일러플레이트 코드를 줄이기 위해 사용하는 라이브러리입니다. @Builder 어노테이션을 사용하면 위와 같은 Builder 패턴을 자동으로 생성해줍니다.
Lombok 사용 예제:
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private String firstName;
private String lastName;
private int age;
private String email;
}
public class Main {
public static void main(String[] args) {
User user = User.builder()
.firstName("John")
.lastName("Doe")
.age(30)
.email("john.doe@example.com")
.build();
System.out.println(user);
}
}