Giter Site home page Giter Site logo

spring-container-study's Introduction

spring-container-study

스프링 컨테이너 공부

spring-container-study's People

Contributors

dongjun-yi avatar

Watchers

 avatar

spring-container-study's Issues

V4 : 컴포넌트 스캔과 자동 의존관계 주입

  • 컴포넌트 스캔 클래스
@Configuration
@ComponentScan(
        basePackages = "com.example.demo",
        excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoConfig {
}
  • 컴포넌트 등록 & 자동 의존관계 주입
@Component
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;

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

V3 버전의 코드에서는 @Configuartion 구성 파일에 개발자가 직접 수동으로 스프링 컨테이너에 Bean을 등록했다. 수동으로 빈을 등록했을 때의 장점은 스프링 컨테이너가 관리하는 Bean들을 한눈에 볼 수 있다는 장점이 있지만 Bean 의 개수가 많아지면 관리하기 불편하게 된다.
따라서 V4 버전에서는 컴포넌트 스캔과 자동 의존관계 주입을 통해 스프링 컨테이너에 Bean을 쉽게 등록할 수 있게 한다.

@ComponentScan : 컴포넌트로 등록된 객체를 스프링 컨테이너에 Bean으로 관리하기 위해 지정된 패키지 경로대로 컴포넌트를 스캔한다.
@Component : Bean으로 등록될 객체를 @Component 를 사용하여 컴포넌트 스캔의 대상으로 등록한다.
@Autowired : 자동의존관계를 주입해주는 기능으로 스프링 컨테이너에 등록된 Bean을 조회하여 생성자에 필요한 인스턴스를 자동으로 주입해준다.

스프링에서 제공해주는 애너테이션을 이용하여 자동으로 Bean을 등록하고 의존관계를 주입해주는 기술을 사용할 수 있다. 컴포넌트 스캔과 자동의존관계 주입을 이용하면 비즈니스 로직 개발에 집중할 수 있고 애플리케이션의 Bean쉽게 관리할 수 있게 된다.

V1 버전의 코드 구현

  • MemberServiceImpl의 코드
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}
  1. 코드를 보면 memberServiceImpl은 인터페이스 MemberRepository를 의존하는 것처럼 보이지만 실제 인스턴스도 같이 참조하는 것이다.
    이는 추상화에 의존해야 한다는 DIP 원칙을 위반한 것으로 위 코드는 추상된 것과 구체적인 것 두개에 모두 의존하는 코드이다.

  2. DIP 원칙 위반으로 연쇄적으로 OCP도 위반하게 된다. 그 이유는 만약 요구사항의 변경으로 repository 구현체가 다른 것으로 변경되면 코드를 수정해주어야 하기 때문이다. 만약 MemoryRepsoitory에서 다른 구현체로 바뀌게 되면 코드를 수정해 주어야 한다.

  3. 클래스의 역할을 보면 비즈니스 로직을 실행하는 역할과 memberRepository 인스턴스를 생성하는 역할을 담당해 하나의 클래스에서 2개의 책임을 갖게 된것이다. 이는 SRP 원칙에 어긋나는 코드 설계이다.


DIP, OCP, SRP 원칙을 지키기 위해 역할을 기준으로 클래스를 분리하자. 비즈니스 로직에 집중하는 클래스와 인스턴스를 생성하는 역할을 나누어 클래스를 설계하면 객체지향 설계 원칙을 지키며 코드를 구현할 수 있다.

V3 버전의 코드

@Configuration
public class SpringAppConfig {

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

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

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

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

순수 자바 코드로 객체를 생성하는 것에서 스프링 애너테이션을 통해 객체를 생성한다.

@Bean은 스프링 컨테이너가 관리하는 객체를 말한다.
@Configuration은 Bean을 생성하고 관리하는 설정 정보 파일로, 스프링 컨테이너가 생성될 때 @Configuration이 붙은 클래스 파일을 참고해 Bean을 생성한다.

만약 기존의 V2버전의 순수 자바코드로 객체를 관리하게 되면 어떻게 될까? 클라이언트의 요청마다 new 연산자를 사용해 객체를 생성하기 때문에 객체가 요청마다 생성되는 문제가 발생한다. 이 문제를 해결하기 위해 @Configuration은 모든 Bean들을 싱글톤으로 관리하여 독립적인 클라이언트의 요청에도 한번 생성한 객체를 반환해준다.

@Configuration이 붙은 클래스도 스프링 컨테이너가 Bean으로 관리하는데, 이는 스프링이 Configuration 라이브러리를 이용해 @Configuration이 붙은 클래스를 상속한 객체를 생성하고 Bean들을 싱글톤으로 관리하게 된다. 만약 위의 코드와 같이 SpringAppConfig.class로 @Configuration을 사용하면 해당 bean의 클래스를 출력해서 보면 class com.example.demo.config.SpringAppConfig$$SpringCGLIB$$0와 같이 출력된다.

싱글톤으로 관리되는 객체 덕분에 수 많은 클라이언트의 요청에도 한번 생성한 객체를 재사용하여 불필요한 객체의 생성을 막아 메모리를 효율적으로 사용할 수 있게 하는 것이다.

V2 버전의 코드 변화

AppConfig.java

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

    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    ....
}

MemberServiceImpl.java

public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;

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

V1 버전의 코드에서는 MemberServiceImpl 클래스가 객체의 생성과 구현 역할 모두 수행하였다. 이는 SRP 원칙에 위배되어 객체의 생성 부분은 따로 AppConfig 클래스에 구성하여 한 클래스에 하나의 책임만 갖도록 구성하였다.

구현체에 의존하여 DIP를 위반했던 MemberServiceImpl는 생성자를 통해 구현체를 넘겨받도록 구성하였다. 그럼 이 구현체는 누가 생성해주고 넘겨주는가? 바로 AppConfig이다. 객체를 생성하는 책임을 갖는 AppConfig는 애플리케이션에 필요한 인자를 주입하고 객체를 생성한다.

이렇게 객체의 생성과 구현을 나누어서 구현 코드인 MemberServiceImpl은 DIP 원칙을 지킬 수 있게 되었다. 이러한 객체의 구현체를 자신이 아닌 외부에서 생성해 객체의 인스턴스를 주입해주는 것을 의존성 주입이라고 한다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.