Spring boot - 3

현곤·2024년 12월 12일
@Configurable
public class BaseInitData {
    @Bean
    public List<Integer> ages() {
        return List.of (10, 20, 30, 40, 50);
    }
}

이게 왜 안될까 하고 보던 중 드디어 찾아냈다 이녀석 @Configurable

@Configurable 이 아닌 @Configuration 을 사용해야 했다.

그래서 이게 무슨 차이일까 하고 알아봤는데,

@Configurable

  • AspectJ 에서 제공하는 애노테이션
  • 도메인 객체(엔티티 등)에 의존성 주입을 가능하게 한다
  • new 키워드로 생성된 객체에도 스프링의 의존성 주입을 적용하고 싶을 때 사용
  • 주로 도메인 주도 설계(DDD)에서 엔티티 객체에 의존성을 주입하는 데 사용
  • 스프링 컨텍스트에서 관리되지 않는 객체에 의존성 주입을 가능하게 한다

@Configuration

  • Spring Framework 의 공식 애노에티션
  • 스프링 빈 구성 및 정의를 위해 사용
  • 메서드에 @Bean 애노테이션을 달아 빈을 생성하고, IoC 컨테이너 에 등록
  • 애플리케이션의 빈 설정과 구성을 담당
  • 스프링 컨텍스트에서 직접 관리되는 클래스를 표시

예시

// @Configuration 예시
@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

// @Configurable 예시
@Configurable
public class User {
    private UserService userService; // new 키워드로 생성해도 의존성 주입 가능

    public User() {
        // 일반적으로는 userService가 null이겠지만, 
        // @Configurable을 사용하면 의존성 주입 가능
    }
}

// 사용 예
User user = new User(); // 스프링 빈으로 관리되지 않아도 의존성 주입 가능

일반적으로는 @Configration 을 많이 사용하고 @Configrable 은 드물게 사용된다.

(@Configrable은 특수한 상황에서만 사용하는 고급 기능)


@GetMapping 에 계속 중복되는 단어가 껴있다?

@RequestMapping 으로 중복되는 단어를 클래스 레벨에 달아주면 된다.

쉽게 말해, 접두어가 있으면 클래스 위에 @RequestMapping 쓰기


또 못찾아서 계속 헤매다가 이것저것 써보니까 됐다

진짜 이것저것 다 해봤음


  1. @Qualifier
@Controller
@RequestMapping("/home2")
@RequiredArgsConstructor
public class Home2Controller {
    @Qualifier("ages")
    private final List<Integer> ages;

    @Qualifier("ages2")
    private final List<Integer> ages2;

    @GetMapping("/ages")
    @ResponseBody
    public List<Integer> ages() {
        return ages;
    }

    @GetMapping("/ages2")
    @ResponseBody
    public List<Integer> ages2() {
        return ages2;
    }
}

이렇게 해봤는데 안됐음


  1. @Primary
@Configuration
public class BaseInitData {
    @Bean
    @Primary
    public List<Integer> ages() {
        return List.of(10, 20, 30, 40, 50);
    }
}

해보니까 되는 줄 알고 설렜는데 age2 를 역순으로 나오게하자 가 목표였는데

@Primary 로 기본 빈 지정을 하면 저 값을 그대로 물려받게 되는 거라

역순이 아닌 10 ~ 50으로 나와서 안됐음


  1. 생성자한테 직접 명시
@RequiredArgsConstructor
public class Home2Controller {
    public Home2Controller(@Qualifier("ages") List<Integer> ages, 
                           @Qualifier("ages2") List<Integer> ages2) {
        this.ages = ages;
        this.ages2 = ages2;
    }
    
    private final List<Integer> ages;
    private final List<Integer> ages2;
}

이것도 안됐음


  1. 컴파일러에 -parameters 플래그 추가하기
tasks.withType<JavaCompile> {
	options.compilerArgs.add("-parameters")
}

바보같이 어제 추가하고 또 추가해서 2줄이 됐는데도 몰랐음


  1. Qualifier로 ages 빈 명시적 주입 받고 이것을 기반으로 ages2 생성
@Configuration
public class Base2InitData {
    @Bean
    public List<Integer> ages2(@Qualifier("ages") List<Integer> ages) {
        List<Integer> reversedList = new ArrayList<>(ages);
        Collections.reverse(reversedList);
        return reversedList;
    }
}

  1. Lombok 대신 직접 생성자 작성
@Controller
@RequestMapping("/home2")
public class Home2Controller {
    private final List<Integer> ages;
    private final List<Integer> ages2;

    public Home2Controller(@Qualifier("ages") List<Integer> ages,
                           @Qualifier("ages2") List<Integer> ages2) {
        this.ages = ages;
        this.ages2 = ages2;
    }

    @GetMapping("/ages")
    @ResponseBody
    public List<Integer> ages() {
        return ages;
    }

    @GetMapping("/ages2")
    @ResponseBody
    public List<Integer> ages2() {
        return ages2;
    }
}

@RequiredArgsConstructor 를 쓰고 싶었는데,
해결 방법이라고 하니 어쩔 수 없이 바꿨음


  1. 컴파일러 플래그 사용

lombok.config 파일을 만들어서 @Qualifier 를 생성자 주입과
함께 사용하도록 설정하는 건데 어떻게 하는 건지 몰라서 또 헤맸음

프로젝트 제일 상위 폴더에 파일 생성해서

lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier

이거 넣는 건데 넣어도 안됐었다


  1. Bean 충돌 확인
@Component
public class BeanPrinter implements ApplicationRunner {
    private final ApplicationContext applicationContext;

    public BeanPrinter(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void run(ApplicationArguments args) {
        System.out.println("Beans in application context:");
        for (String beanName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanName);
        }
    }
}

이거 써서 충돌 확인하는 건데 하는 방법 몰라서 안해봤읍니다

그래서 계속 삽질하다가 왜 안될까 하고 설정창 열어서 확인해보니까

이걸 안했었음 ㅎㅎ..ㅋㅋ..

저거 하니까 귀신같이 됐읍니다.....................


ApplicationRunner

ApplicationRunner 는 Spring Boot에서
애플리케이션이 시작된 직후(ApplicationContext가 완전히 초기화 된 후)에
실행되는 콜백 인터페이스다.

즉, baseInitDataApplicationRuner 메서드는
애플리케이션 시작 지점에 정의된 작업을 실행하도록 구성된 것

  • 위치 : Spring Boot의 라이프 사이클에서, 모든 빈이 초기화되고 애플리케이션이
    준비 상태가 된 뒤 실행된다.

  • 역할 : 초기 데이터를 설정하거나 부가 작업을 수행하는 데 자주 사용된다.

쉽게 말해,

프로그램을 시작할 때 자동으로 실행되는 작업을 정의한 것

코드가 실행되는 흐름

(1) @Bean 으로 등록된 메서드 호출

Spring Boot 는 애플리케이션이 시작될 때, @Configration 클래스나
다른 @Bean 메서드에서 선언된 빈을 생성한다.

baseInitDataApplicationRunner 메서드가 호출되어
ApplicationRunner 빈이 생성된다.

프로그램이 켜질 때 Spring이
baseInitDataApplicationRunner 라는 코드를 찾아 실행해준다.


(2) ApplicationRunner.run 메서드 실행

Spring Boot는 애플리케이션 초기화가 완료된 후,
ApplicationRunnerrun 메소드를 호출한다.

  • 여기서 args 는 애플리케이션 실행 시 전달된 명령줄 인수를 나타냄

  • 작성한 람다식 args -> { . . . }run 메서드의 구현

(3) wiseSayingService.write 호출

wiseSayingService.write 메서드가 연속으로 호출된다.

이 메서드는 특정 작업(예 : 데이터 저장, 출력 등)을 수행한다.

  • 메서드 동작 : wiseSayingService.write("내용", "작성자") 에서
    "내용""작성자" 가 매개변수로 전달된다.

  • 이 동작의 구체적인 결과는 wiseSayingService 가 정의된 클래스와
    write 메서드의 구현에 따라 달라진다.

예를 들어,

"내용 1" 이라는 문장과 "작성자 1" 라는 이름을 저장하거나
출력하는 일을 할 수도 있다.

(4) 결과

wiseSayingSerivce.write 메서드가 데이터를 DB에 저장하거나, 콘솔에 출력하거나,
메모리에 기록하는 등의 작업을 수행한다.

초기화 데이터를 저장하는 작업일 가능성이 크다.

프로그램이 시작될 때마다,
이 코드에 있는 작업이 자동으로 실행돼서 데이터가 등록되거나 어떤 일이 처리된다.


예상 동작

  1. wiseSayingService 정의

wiseSayingService 는 아마도 어딘가에 정의된
Spring Bean (예 : @Service 로 등록된 클래스) 일 것이다.
이 클래스의 write 메서드가 실제 동작을 결정한다.

  1. write 메서드 구현

만약 wiseSayingService.write 메서드가 콘솔 출력이나 DB 저장과 관련되었다면

  • 콘솔에 데이터가 출력될 수 있다.
  • 또는 DB에 초기화 데이터가 추가될 수 있다.
  1. 결과 예시

예를 들어, write 메서드가 로그를 출력한다면, 실행 결과는 다음과 비슷할 수 있다.

내용1 - 작성자1 저장됨.
내용2 - 작성자2 저장됨.
내용3 - 작성자3 저장됨.

실행 과정 요약

  1. Spring Boot 애플리케이션이 시작된다.

  2. @Bean 으로 등록된 baseInitDatteApplicationRunner
    Application Context에 등록된다.

  3. 애플리케이션 초기화 후, Spring Boot가 ApplicationRunner
    run 메소드를 실행한다.

  4. wiseSayingService.write 메서드가 호출되어 정의된 작업을 수행


사용 목적

  • 초기 데이터를 데이터베이스에 삽입

  • 기본 설정이나 테스트 데이터를 로드

  • 특정 서비스의 초기화 작업 실행

정말 간단하게 요약하자면 프로그램 켜질 때 실행되는 초기 작업 코드다.

"명언 저장하기" 같은 작업을 미리 해두는 것

profile
코딩하는 곤쪽이

0개의 댓글