[TIL] 2024-08-12

성장일기·2024년 8월 17일

회고

목록 보기
27/37

중요 학습 내용 [Spring]

Spring

DI

  • 객체의 의존성을 외부에서 주입하는 방식

Field injection

해당 객체가 생성된 이후, JAVA reflection 기술로 필드에 값을 주입한다.

  • 장점:
    • 매개변수 생성자를 만드는 것이 번거롭기에, 테스트 코드상에서 편하게 사용한다.
  • 단점:
    • 순환참조를 컴파일 시점에 확인할 수 없다.
    • mutable: 호출한 클래스에서 private 필드에 값을 주입하기에, 주입 시점에 필드를 변경해야한다.

Setter injection

  • 클래스 필드를 setter를 통해 주입

  • 단점:

    • 순환참조를 컴파일 시점에 확인할 수 없다.
    • mutable: 생성자 호출 이후 setter 메서드 호출로 주입된다.
    • 메서드 이름만 봐도 어떤 필드, 타입이 있는지 알 수 있다. 따라서 setter는 anti-parttern이다.
    • 특정 클래스를 값으로 칠 때, 특정 클래스의 필드가 setter로 변경이 되면, 변경된 클래스의 주소값변경된 클래스의 주소값이 동일하기에, 일반적인 객체 비교를 할 수 없다.
      • java의 객체지향적 관점에서, 객체는 불변객체로 설정하는 것지향하는 바이다.
    • 필드의 갯수가 많아지면 남용될 수 있기에, 좋지 않다.
    • 생성자 전까지 필드에 대해 초기화를 해야하기에, 해당 필드를 불변객체로 가질 수 없다.

Constructor injection

  • 클래스 필드를 생성자를 통해 주입

  • 장점:

    • 순환참조를 컴파일 시점에 확인할 수 있다. 따라서 순환참조를 개발시점에 방지할 수 있다.
    • 테스트 코드 간편작성 가능(Mock 객체 생성 불필요)
    • 필드에 final 키워드를 사용할 수 있다.
    • immutable: 생성자 호출 시점에 주입받은 필드가 더이상 변경되지 않는다는 확신을 가질 수 있다. 따라서 개발 시, 특정 클래스의 필드값을 확신할 수 있다.

순환참조
: 두 객체가 서로를 의존하고 있을 때 발생

해결방법
: 중간객체를 두어 두 클래스가 서로 중간객체를 필드로 갖게끔 한다.
ex. Board, Member
Board: MemberVO(중간객체), ...
Member: BoardVO(중간객체), ...

Other Annotations

@Qualifier

  • 동일한 타입의 여러 빈이 있을 때, 특정 빈을 주입하도록 지정하는 데 사용
  • @Autowired와 함께 사용됨
@Service
public class PokemonService {

    // 설명. 필드 주입
    @Autowired
    @Qualifier("squirtle")
    private Pokemon pokemon;

    // 설명. 생성자 주입
    @Autowired
    public PokemonService(@Qualifier("charmander") Pokemon pokemon) {
        this.pokemon = pokemon;
    }

    public void pokemonAttack() {
        pokemon.attack();
    }
}

@Resource

  • 이름, 타입 또는 둘 다를 기준으로 의존성을 주입

@Service
public class PokemonService {

    // 설명. Pokemon(type)으로 처리 시
    @Resource(name="charmander")
    private Pokemon pokemon;

    @Resource(name="charmander")
    private List<Pokemon> pokemonList;


    public void pokemonAttack() {
        pokemonList.forEach(Pokemon::attack);
    }
}

@Inject

  • Java 표준 의존성 주입 애노테이션
  • @Autowired와 거의 동일하게 동작

@Service("pokemonServiceInject")
public class PokemonService {

    // 설명.  필드 주입 가능
    @Inject
    @Named("squirtle")
    private Pokemon pokemon;

    // 설명. 생성자 주입 가능
    @Inject
    public PokemonService(@Named("charmander") Pokemon pokemon) {
        this.pokemon = pokemon;
    }

    // 설명. setter 주입 가능
    @Inject
    public void setPokemon(@Named("pikachu")Pokemon pokemon) {
        this.pokemon = pokemon;
    }


    public void pokemonAttack() {
        pokemon.attack();
    }
}

Bean scope

scope

  • 스프링 bean이 인스턴스를 생성하는 시점 및 유지 기간을 설정가능

singleton: 하나의 인스턴스만을 생성하며 해당 인스턴스가 공유된다.

@Configuration
public class Configuration {

    ...

    @Bean
    @Scope("singleton") // 스프링 컨테이너(IOC Container)는 bean 객체를 기본적으로 싱글톤으로 관리한다. (생략 가능)
    public Shopping cart() {
        return new ShoppingCart();
    }

}

prototype: 매번 bean이 필요로 할 때 새로 인스턴스를 생성한다.

@Configuration
public class Configuration {

    ...

    @Bean
    @Scope("prototype") // bean 객체의 life cycle을 다른 주기로 가져갈 수 있다
    public Shopping cart() {
        return new ShoppingCart();
    }

}

사실 Tomcat이 사용자마다 thread를 할당하고 thread마다 객체를 필요로 하기에, 전체 사용자가 공용으로 사용하는 객체는 개념상 분리되어있다. 따라서 아래의 request, session, globalSession은 사용할 일이 거의 없다.

request:

  • 수명 주기: HTTP 요청 시작 시 생성, 요청 종료 시 소멸됩
  • 용도: 사용자별 데이터 처리, 요청에 종속적인 데이터 저장에 적합
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
    // 필드 및 메서드 정의
}

session: http 세션 당 하나의 인스턴스를 새로 생성하고 세션이 종료되면 인스턴스를 파기

  • 웹 애플리케이션 컨텍스트에만 해당
  • 수명 주기: HTTP 세션 시작 시 생성, 세션 종료 시 소멸
  • 용도: 사용자 로그인 정보, 쇼핑 카트 등 세션 범위의 데이터 저장에 적합
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
    // 필드 및 메서드 정의
}

globalSession: 전역 세션 당 하나의 인스턴스를 생성하고 전역 세션이 종료되면 인스턴스를 파기

  • 포털 애플리케이션 컨텍스트에만 해당
  • 전자정부 프레임워크에서 사용
  • 수명 주기: 포털 세션 시작 시 생성되고, 포털 세션 종료 시 소멸됩니다.
  • 용도: 포털 애플리케이션 전역에서 공유되는 세션 데이터 저장에 적합합니다.
@Component
@Scope(value = WebApplicationContext.SCOPE_GLOBAL_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class GlobalSessionScopedBean {
    // 필드 및 메서드 정의
}

Property

I18N

  • i18n 소프트웨어의 국제화(Internationalization, I와 n사이 18글자)에서 처리해야 할 작업들
  1. 언어, 지역별 번역
  2. OS/Platform 인코딩
  3. 문자열 치환 방법
  4. 국제화 UI(문자열 크기 변화, 폰트, 아이콘 등)
  5. 쓰기 방향의 차이
  6. 숫자, 공백, 화폐, 날짜 주소, 측정 단위 등
  7. 타임존, 썸머타입 등 시각
  8. 문자열 정렬 방법
public class Application {
    public static void main(String[] args) {


        // 다국어 메시지 처리
        ApplicationContext context = new AnnotationConfigApplicationContext(ContextConfiguration.class);

        String error404MessageKR = context.getMessage("error.404", null, Locale.KOREA);
        String error500MessageKR = context.getMessage("error.500", new Object[] {"나", new Date()}, Locale.KOREA);

        String error404MessageUS = context.getMessage("error.404", null, Locale.US);
        String error500MessageUS = context.getMessage("error.500", new Object[] {"ME", new Date()}, Locale.US);

        System.out.println("error404MessageKR: " + error404MessageKR);
        System.out.println("error500MessageKR: " + error500MessageKR);
        System.out.println("error404MessageUS: " + error404MessageUS);
        System.out.println("error500MessageUS: " + error500MessageUS);
    }
}
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

@Configuration
public class ContextConfiguration {

    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {

        // 설명. 접속하는 세션의 로케일(Locale)에 따라 자동 재로딩하는 용도의 MessageSource 구현체
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();

        // 설명. 다국어 메시지를 읽어올 properties 파일의 파일 이름 설정
        messageSource.setBasename("section03/properties/subsection02/i18n/message");

        // 설명. 불러온 메시지를 지정 시간 동안 캐싱(초 단위)
        messageSource.setCacheSeconds(10);

        // 설명. 기본 인코딩 셋 설정
        messageSource.setDefaultEncoding("UTF-8");

        return messageSource;
    }

}
../i18n
    - message_ko_KR.properties
    - message_ko_KR.properties
    ...
profile
엔지니어로의 성장일지

0개의 댓글