[Spring] 스프링 핵심 원리 - 기본편 강의정리 1

하파타카·2023년 11월 22일
0
post-thumbnail

스프링 핵심 원리 - 기본편
인프런 김영한님 Spring 로드맵 2번 강의

섹션1. 객체 지향 설계화 스프링 ~ 섹션4. 스프링 컨테이너와 스프링 빈


스프링 핵심 원리 - 기본편

섹션1. 객체 지향 설계와 스프링

스프링이란?

  • 자바를 기반으로 한 프레임 워크로, 좋은 객체 지향 프로그램을 개발할 수 있도록 도와주는 프레임워크임.

스프링 부트

  • 단독으로 실행할 수 있는 스프링 애플리케이션을 쉽게 생성가능

객체 지향

  • 역할과 구현으로 구분하면 구조가 단순해지고 유연해지며 변경도 편리해짐.
  • 자바 언어의 다형성을 활용
    • 역할 = 인터페이스
    • 구현 = 인터페이스를 구현한 클래스, 구현 객체
  • 객체 설계 시 역할과 구현을 명확히 분리
  • 객체 설계 시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기

실생활 예시 1

  • 운전자 역할(인터페이스) - 자동차 역할(구현체)
    운전자는 자동차가 바뀌어도 자동차의 역할만 그대로라면 영향이 없음.
    운전자가 자동차 내부를 자세히 알지 않아도 됨.
    새로운 자동차가 출시되어도 운전자(클라이언트)는 새로 배울 필요가 없음.

실생활 예시 2

  • 로미오와 줄리엣 공연
    로미오 역할(배우1, 배우2) - 줄리엣 역할(배우3, 배우4)
    배우에게 영향이 생겨도 로미오, 줄리엣 역할을 다른 배우로 바꿔 유연하게 대처할 수 있다.

다형성의 본질

  • 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
  • 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.

역할과 구현을 분리할 때의 한계

  • 역할(인터페이스) 자체가 변하면 클라이언트, 서버 모두에 큰 변경이 발생
  • 인터페이스를 안정적으로 잘 설계하는 것이 중요 = 안정적인 인터페이스란 변경을 최소화 한 인터페이스

좋은 객체 지향 설계의 5가지 원칙(SOLID)

1. SRP 단일 책임 원칙

  • 한 클래스는 하나의 책임만 가져야 한다.
  • 단일 책임의 기준은 변경. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것.
    • 예) UI 변경, 객체의 생성과 사용을 분리

2. OCP 개발-폐쇄 원칙

Open/closed principle

  • 소프트 웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다
  • 다형성을 적극 활용
  • 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현

3. LSP 리스코프 치환 원칙

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
    • 예) 자동차 인터페이스의 엑셀은 앞으로 가는 기능. 뒤로 가게 하면 LSP 위반. 느리더라도 앞으로 가야함.

4. ISP 인터페이스 분리 원칙

  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다
  • 분리하면 인터페이스 자체가 변해도 클라이언트에 영향을 주지 않음

5. DIP 의존관계 역전 원칙

  • 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 의미
  • 역할에 의존해야 한다는 말과 같음
    • 운전자는 아반떼의 역할에 대해서 아는 게 아니라 자동차 역할에 대해서 알아야 함

정리

// MemoryMemberRepository에서 JdbcMemberRepository로 변경할 때
// 아래와 같은 경우 Service인터페이스에 코드변경이 불가피함.
public class MemberService {
//	private MemberRepository memberRepository = new MemoryMemberRepository();	변경 전
	private MemberRepository memberRepository = new JdbcMemberRepository();	// 변경 후
}
  • 객체 지향의 핵심은 다형성
  • 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경됨
  • 다형성 만으로는 OCP, DIP를 지킬 수 없음
  • 뭔가 더 필요함...

의존이란?
A가 B를 알고있다면 A는 B에 의존한다고 표현한다.
=> A가 B를 알고있는 상태에서 B에 변경이 생기면 A에도 변경이 필요하기 때문이라 생각하면 됨.

섹션2. 스프링 핵심 원리 이해1 - 예제 만들기

가상 시나리오를 바탕으로 비즈니스 요구사항 정리

- 회원
  - 회원을 가입하고 조회할 수 있다.
  - 회원은 일반과 VIP 두 가지 등급이 있다.
  - 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)
- 주문과 할인 정책
  - 회원은 상품을 주문할 수 있다.
  - 회원 등급에 따라 할인 정책을 적용할 수 있다.
  - 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
  - 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루
고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)

=> 회원 저장소 역할과 할인 정책 역할을 추상화, 이를 구현하는 구현체를 만들어 적용하면 OCP와 DIP를 지키는 프로그램을 만들 수 있다.



VIP회원의 주문에만 정액 할인 정책 1000원을 적용하는 단계까지 만든 상황에서의 프로젝트 구조.
변동 가능성이 높은 할인 정책, DB 연동 기능은 구현체를 이용하여 쉽게 갈아끼울 수 있도록 구조가 설계되어 있다.

섹션3. 스프링 핵심 원리 이해2 - 객체 지향 원리 적용

새로운 할인 정책 적용과 문제점

할인 정책 개편에 따라 정액 할인 정책을 정률 할인 정책으로 변경한다.

- OrderService -

public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);
}

- OrderServiceImpl -

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
//    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

위와 같이 OrderServiceImpl에서 클라이언트인 DiscountPolicy인터페이스만 의존하는 것이 아닌 구체 클래스인 RateDiscountPolicy클래스도 의존하고 있음을 확인할 수 있다.
결국 FixDiscountPolicyRateDiscountPolicy로 변경하면 DIPOCP를 모두 위반하게 된다.

관심사의 분리

공연으로 비유한다면...

  • 로미오와 줄리엣 공연을 한다고 했을 때 위의 코드는 로미오 역할의 배우가 직접 줄리엣 역할을 수행할 배우를 초빙해오는 것과 같다.
    • 이 경우 로미오 역 배우는 배우의 역할뿐 아니라 다양한 책임을 가지게 된다.
  • 역할에 따른 분리 필요 : 배우와 별개의 책임을 가진 기획자라는 역할을 추가한다.
    • AppConfig클래스를 이용하여 생성자 주입을 하도록 변경.

final키워드와 멤버변수 초기화

final키워드는 변수의 값을 변경하지 못하게 만듦(=상수).
상수는 사용 전 반드시 초기화가 선행되어야 함.
이때 인스턴스 변수에 final키워드가 사용될 경우 생성자에서 초기화가 가능함.

AppConfig를 통한 생성자 주입

구현체를 변경할 경우

- MemberServiceImpl -

public class MemberServiceImpl implements MemberService{
    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }
    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

- OrderServiceImpl -

public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

클라이언트에 영향을 주지 않고 구현체만 바꿀 수 있도록 변경.
final키워드로 인스턴스 변수 초기화 시 생성자를 통한 초기화가 가능하게 한 후 AppConfig클래스에서 해당 구현체를 선택하여 생성하도록 함.

- AppConfig -

public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    private MemoryMemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }
}

=> 이와 같은 구조로 설계하면 구현체를 다른 것으로 변경해도 AppConfig클래스만 변경하면 된다.
AppConfig클래스는 구성 영역을, 나머지는 사용 영역을 담당하게 되는 셈.

스프링으로 전환하기

- AppConfig -

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public MemoryMemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}

- MemberApp -

public class MemberApp {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService", MemberService.class);

        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("member = " + member.getName());
        System.out.println("findMember = " + findMember.getName());
    }
}

- OrderApp -

public class OrderApp {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        OrderService orderService = ac.getBean("orderService", OrderService.class);

        Long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "itemA", 20000);
        System.out.println("order = " + order);
    }
}

앞서 작성했던 코드를 위와 같이 변경한다.
여기서 사용된 @BeanApplicationContext에 대해서는 개념정리가 필요하므로 아래 섹션4에서 정리하도록 한다.

섹션4. 스프링 컨테이너와 스프링 빈

스프링 컨테이너(Spring Container)란?

스프링 컨테이너 생성 과정

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
  • 위 예제에서 ApplicationContext가 스프링 컨테이너에 해당한다. 정확히 일치하는 개념은 아니지만 요즘은 거의 동일시 하는 편이다.
  • ApplicationContext는 인터페이스로, AnnotationConfigApplicationContext는 구현체가 된다.
    • AnnotationConfigApplicationContext는 어노테이션 기반으로 스프링을 구성할때 사용하는 구현체이다.
    • 만약 xml등 다른 방법을 사용한다면 그에 맞는 구현체를 사용하여야 하나, 요즘은 대부분 어노테이션을 이용한다.
  • 스프링 컨테이너 내부에는 객체들이 들어있다.
    • 원래는 BeanFactory라는 최상위 객체와 ApplicationContext를 구분해서 사용했다.
    • 현재는 BeanFactory를 직접 사용하는 경우가 드물어 스프링 컨테이너 = ApplicationContext라고 보면 된다.

Bean이란?

  • 스프링에서 관리하는 자바 객체를 말한다.
  • @Bean 어노테이션을 이용하면 해당 객체를 스프링 컨테이너에 등록하여 관리하도록 한다.
  • 빈의 이름은 중복이 없어야 한다. 여러 문제가 생길 수 있으니 중복 자체를 배제하도록 하자.

등록된 빈 확인하기

테스트 클래스 생성하여 현재 스프링 컨테이너에 등록된 빈을 확인해보는 예제.

public class ApplicationContextInfoTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("모든 빈 출력하기")
    void findAllBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = ac.getBean(beanDefinitionName);
            System.out.println("name = " + beanDefinitionName + "object = " + bean);
        }
    }

    @Test
    @DisplayName("애플리케이션 빈 출력하기")
    void findApplicationBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = " + beanDefinitionName + "object = " + bean);
            }
        }
    }
}


findApplicationBean()메서드를 실행시킨 결과.
AppConfig클래스에서 등록한 빈과 @Service, @Configuration 어노테이션을 이용해 등록한 빈들을 직접 확인할 수 있다.

findAllBean() 메서드 실행 시 스프링 내부에서 등록된 빈까지 모두 확인이 가능하다. (사진은 생략)

스프링 빈 조회 - 1) 기본 조회

public class ApplicationContextBasicFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 이름으로 조회")
    void findBeanByName(){
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }
    @Test
    @DisplayName("이름없이 타입으로만 조회")
    void findBeanByType(){
        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }
    @Test
    @DisplayName("구체타입으로 조회")
    void findBeanByName2(){
        // 구체타입을 조회 시 유연성이 떨어질 수 있음에 유의.
        MemberServiceImpl memberService = ac.getBean(MemberServiceImpl.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }
    @Test
    @DisplayName("빈이름으로 조회x")
    void findBeanByNameX(){
//        MemberService memberService = ac.getBean("xxx", MemberServiceImpl.class);
//        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
        // assertThrows : 우측 로직에서 발생한 오류가 좌측에 기입한 오류와 같으면 테스트 성공. 오류안나도 실패.
        assertThrows(NoSuchBeanDefinitionException.class, ()-> ac.getBean("xxx", MemberService.class));
    }
}

테스트 영역에 새로 클래스를 만들어 확인.
등록된 빈을 검색한다.
이때 이름으로 조회, 타입으로 조회, 구체타입으로 조회 등 다양한 조건으로 조회할 수 있다.

맨 아래의 findBeanByNameX()메서드는 실패 테스트를 위한 것으로, assertThrows(발생할 것으로 예상되는 Exception클래스, Exception을 발생시킬 코드);의 형식으로 테스트한다.
코드실행시 예상한 Exception이 발생하면 테스트 성공.

스프링 빈 조회 - 2) 동일한 타입이 둘 이상일 때

public class ApplicationContextSameBeanFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입으로 조회 시 같은 타입이 둘 이상 있으면 중복 오류가 발생한다.")
    void findBeanByTypeDuplicate() {
        assertThrows(NoUniqueBeanDefinitionException.class, ()->ac.getBean(MemberRepository.class));
    }

    @Test
    @DisplayName("타입으로 조회 시 같은 타입이 둘 이상 있으면 빈 이름을 지정하면 된다.")
    void findBeanName() {
        MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
        assertThat(memberRepository).isInstanceOf(MemberRepository.class);
    }

    @Test
    @DisplayName("특정 타입을 모두 조회하기")
    void findAllBeanByType(){
        Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value = " + beansOfType.get(key));
        }
        System.out.println("beansOfType = " + beansOfType);
        assertThat(beansOfType.size()).isEqualTo(2);
    }

    @Configuration
    static class SameBeanConfig {
        @Bean
        public MemberRepository memberRepository1() {
            return new MemoryMemberRepository();
        }
        @Bean
        public MemberRepository memberRepository2() {
            return new MemoryMemberRepository();
        }
    }
}


findAllBeanByType()메서드 실행결과.

타입으로 빈을 찾을 때 동일한 타입이 2개이상이면 빈 이름을 지정해 찾거나 특정 타입을 모두 검색하면 된다.

참고
class 내에서 static class 사용했다는 것은 이 클래스 내에서만 해당 클래스를 사용한다는 의미를 가짐.

스프링 빈 조회 - 3) 상속 관계

  • 자바 빈 조회 시 부모 타입을 조회하면 자식 타입도 같이 조회된다.
  • 자바 최상위 객체인 Object타입을 조회하면 모든 스프링 빈을 조회한다.
public class ApplicationContextExtendsFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

    @Test@DisplayName("부모 타입으로 조회 시 자식이 둘 이상 있으면 중복 오류가 발생한다.")
    void findBeanByParentTypeDuplicate(){
        DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
        assertThrows(NoUniqueBeanDefinitionException.class, ()->ac.getBean(DiscountPolicy.class));
    }
    @Test@DisplayName("부모 타입으로 조회 시 자식이 둘 이상 있으면 빈 이름을 지정하면 된다.")
    void findBeanByParentTypeBeanName(){
        DiscountPolicy reteDiscountPolicy = ac.getBean("reteDiscountPolicy", DiscountPolicy.class);
        assertThat(reteDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
    }
    @Test
    @DisplayName("특정 하위 타입으로 조회하기")
    void findBeanBySubType() {
        RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
        assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
    }

    @Test
    @DisplayName("부모 타입으로 모두 조회하기")
    void findAllBeanByParentType(){
        Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
        assertThat(beansOfType.size()).isEqualTo(2);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value = " + beansOfType.get(key));
        }
    }

    @Test
    @DisplayName("부모 타입으로 모두 조회하기 - Object")
    void findAllBeanByObjectType(){
        Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value = " + beansOfType.get(key));
        }
    }

    @Configuration
    static class TestConfig {
        @Bean
        public DiscountPolicy reteDiscountPolicy(){
            return new RateDiscountPolicy();
        }
        @Bean
        public DiscountPolicy fixDiscountPolicy(){
            return new FixDiscountPolicy();
        }
    }
}

업로드중..
findAllBeanByParentType()메서드 실행결과.

실제로 개발과정에서 직접 빈 객체를 찾을 일은 거의 없지만 빈 객체를 조회했을때 상속 관계에서는 자식 타입까지 모두 조회된다, 빈 이름 혹은 타입으로 검색할 수 있다는 등의 개념을 알아두어야 하기 때문에 빈 검색 실습을 진행하였음.

Bean Factory

  • Bean FactoryApplication Context스프링 컨테이너라 한다.

1) Bean Factory

  • 스프링 컨테이너의 최상위 객체
  • 스프링 빈을 관리하고 조회하는 기능을 담당한다
  • getBean()을 제공한다
  • 앞서 실습에서 사용한 대부분의 기능은 Bean Factory가 제공하는 기능이다
  • 실제로 Bean Factory를 직접 사용할 일은 거의 없다.
    대부분 부가 기능이 포함된 Application Context를 사용한다.

2) Application Context

  • Bean Factory의 기능을 모두 상속받아 제공한다
  • Bean Factory의 빈을 관리하고 검색하는 기능에 더해 수많은 부가기능을 제공한다
  • Application ContextBean Factory만 아니라 MessageSource, EnvoironmentCapable, ApplicationEventPublisher 등 다양한 인터페이스를 상속받아 제공한다

자바 코드, XML 사용

  • 스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있도록 유연하게 설계되어 있다.
    • 자바 코드, XML, Groovy 등

1) 어노테이션 기반 자바 코드 설정 사용

  • 앞서 실습했던 방법.
  • new AnnotationConfigApplicationContext(AppConfig.class)
  • AnnotationConfigApplicationContext클래스를 사용하면서 자바 코드로 된 설정 정보를 넘기면 된다.

2) XML 설정 사용

  • 최근에는 스프링 부트를 사용하기에 대부분 어노테이션을 사용하지만, 레거시 프로젝트의 경우 XML을 사용한 경우가 많다.
    또한 XML을 이용하면 컴파일 없이 설정 정보를 변경할 수 있다는 장점이 있다.
public class XmlAppContext {

    @Test
    void xmlAppContext(){
        ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}

- appConfig.xml -

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="memberService" class="hello.core.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
    </bean>
    <bean id="memberRepository"
          class="hello.core.member.MemoryMemberRepository" />
    <bean id="orderService" class="hello.core.order.OrderServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository" />
        <constructor-arg name="discountPolicy" ref="discountPolicy" />
    </bean>
    <bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy" />
</beans>

xml로 설정한 내용과 앞서 실습에서 사용한 AppConfig.class에서 설정한 내용은 동일하다.

스프링 빈 설정 메타 정보 - BeanDefinition

스프링은 BeanDefinition이라는 것으로 스프링 메타 정보를 추상화 한다.

스프링 빈을 만드는 방법은 크게 두 가지.

  • 직접적으로 스프링 빈을 등록 => 예) xml을 이용한 방법
  • Factory Bean을 이용해 등록 => 예) 어노테이션을 이용한 방법

인텔리제이 단축키

window os 기준 단축키

  • 리스트 형태의 객체가 있을 때 자동으로 for문 생성 : 객체 아래서 iter 입력 후 Teb 클릭
  • 선택한 영역을 영역 바로 아래 붙여넣기 : ctrl + D
  • import static : alt + Enter
  • 이전에 작업한 파일으로 이동 : ctrl + E
  • 코드 컴플리션으로 넘어가기 : ctrl + shift + Enter
  • 객체 생성 시 좌항 자동완성 : alt + Enter

🔗참고자료

도서 - 웹 개발 워크북 (구멍가게 코딩단 저)

생성자를 이용한 Final 멤버변수 초기화
[Spring] 스프링 빈(Bean)이란 무엇인가?

profile
천 리 길도 가나다라부터

0개의 댓글