본문 바로가기
Back-End/🌱 Spring Boot (java)

[Spring 입문] 스프링 빈(Bean)과 의존관계 설정(Dependency Injection)

by 코딩하는 동현😎 2023. 1. 20.

스프링 빈 (Spring Bean)

스프링 빈이란 스프링 컨테이너에서 관리되는 자바 객체를 의미합니다.

저번에 만든 서비스와 레포지토리를 스프링이 알아볼수 있게 빈으로 등록할 수 있습니다


스프링 컨테이너

스프링 컨테이너는 스프링 빈의 생명 주기를 관리하고, 스프링 빈들에게 추가적인 기능을 제공하고, DI(Dependency Injection 의존관계 주입)와 IoC원리가 스프링 컨테이너에 적용됩니다.

저번 포스트에서는 new 연산자, 인터페이스 호출 등등으로 의존성 주입하고 객체를 생성했었습니다.

스프링 컨터이너를 사용하면 스프링이 알아서 다 관리해줍니다.


스프링 빈 등록 방식

스프링 등록 방식에는 총 두가지 방법이 있고, 두가지 방법 다 중요합니다!

편리한건 컴포넌트 스캔이지만, Configuration파일이 코드 변경에는 더 좋습니다.

 

1. 컴포넌트 스캔

컴포넌트 스캔은 @Component를 명시하여 빈을 추가하는 방법입니다.

@Componet 어노테이션을 사용하면 전부 스프링에 등록이 됩니다.

사실 @Controller , @Service , @Repository, @Configuration 등등의 어노테이션은 이미 @Component가 내장되어있기 때문에 모두 스프링 빈에 등록됩니다.

  • @Controller
    • 스프링 MVC 컨트롤러로 인식됩니다.
  • @Repository
    • 스프링 데이터 접근 계층으로 인식하고 해당 계층에서 발생하는 예외는 모두 DataAccessException으로 변환합니다.
  • @Service
    • 특별한 처리는 하지 않으나, 개발자들이 핵심 비즈니스 계층을 인식하는데 도움을 줍니다.
  • @Configuration
    • 스프링 설정 정보로 인식하고 스프링 빈이 싱글톤을 유지하도록 추가 처리를 합니다. 여기서 따로 컴포넌트말고 다른 방식으로 빈을 관리 할 수 있습니다.

저번 프로젝트에서 각 클래스 빈으로 등록하기

https://konkukcodekat.tistory.com/entry/Spring-%EC%9E%85%EB%AC%B8-%EA%B3%84%EC%B8%B5-%EA%B5%AC%EC%A1%B0%EC%99%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84%EC%A3%BC%EC%9E%85-Dependency-Injection-%EC%8B%A4%EC%8A%B5

 

[Spring 입문] 계층 구조와 클래스 의존관계/주입 (Dependency Injection) 실습

입문자의 비즈니스 실습 요구 사항 정리 데이터 : 회원 ID , 이름 기능 : 회원 등록, 조회 아직 데이터저장소가 없다고 가정하고, 자바내부 자료구조를 이용해서 모의로 실습 해볼 것입니다. 서비

konkukcodekat.tistory.com

스프링 빈 이용시 : 각 클래스 생성자에 @Autowired 를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입합니다. 생성자가 1개만 있으면 @Autowired 는 생략할 수 있다.

 

 

@Repository 추가

repository/MemoryMemberRepository.java

package example.boot.repository;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ArrayList;


import example.boot.domain.Member;

@Repository
public class MemoryMemberRepository implements MemberRepository {

    // 아직 데이터베이스를 연결 안했으므로 간이로 데이터베이스(store해시맵) 과 id를 일일히 할당해줍시다
    // 데이터베이스가 자동으로 id를 할당하지만 아직 연결 안했으므로 id  1씩 증가하면서 하겠습니다.
    private static Map<Long, Member> store = new HashMap<Long,Member>();
    private static Long sequence = 0L;
	

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        // Optional은 null 반환할 수도 있는데 그대로 반환 시킨다. -> 프론트에서 null 처리
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Member> findByName(String name) {
        // value들, 즉 멤버들을  stream으로 하나씩 확인하는데 filter 람다식으로 이름이 같은것만 고릅니다
        // findAny로 일치하는 것을 반환합니다.
        return store.values().stream().filter(m -> m.getName().equals(name)).findAny();
    }

    @Override
    public List<Member> findAll() {
        // 저장된 값들을 arrayList로 변환해서 반응
        return new ArrayList<Member>(store.values());
    }
    public void clearStore(){
        store.clear();
    }
    
}

 

 

 

 

 

@Service 추가

위에 레포지토리가 스프링 빈으로 컨테이너에 올라와있는 상태에서, @AutoWired를 통해 레포지토리를 스프링 컨테이너로 부터 아주 편하게 받을 수 있습니다.

 

service/MemberService.java

package example.boot.service;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import example.boot.domain.Member;
import example.boot.repository.MemberRepository;

@Service
public class MemberService {
    
    private MemberRepository memberRepository;

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

    // 회원 가입
    public Long join(Member member){
        // 같은 이름이 있는 중복 회원 X
        validateOverapMember(member);
        memberRepository.save(member);
        return member.getId();
    }

    // 중복된 회원이 있는지 검증해주는 함수
    public void validateOverapMember(Member member){
        // Optional 객체 이므로 ifPresent 함수를 이용해서 null 조건문 없이 작성 할 수 있습니다
        memberRepository.findByName(member.getName()).ifPresent(m -> {
            throw new IllegalStateException("already Exists");
        });
    }

    public List<Member> findAll() {
        return memberRepository.findAll();
    }

    public Optional<Member> findOne (Long id){
        return memberRepository.findById(id);
    }

}

2. Configuration 자바 파일로 등록

Java 코드로 빈을 등록할 수 있는데, 클래스를 생성하고, 위에서 언급한 @Configuration 어노테이션을 활용합니다.

각각 그 객체를 반환(생성)하는 메소드를 작성합니다.

이때, 빈을 등록하기 위해 인스턴스를 생성하는 메소드 위에 @Bean를 명시하면 됩니다.

 

src/java/boot/SpringConfig.java

package example.boot;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import example.boot.repository.MemberRepository;
import example.boot.repository.MemoryMemberRepository;
import example.boot.service.MemberService;


@Configuration
public class SpringConfig {

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

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

위에와 같이 등록할 수 있는데, 이렇게 자바코드로 등록하는 이유는 변경이 용이하기 때문입니다.

만약에 메모리 방식을 mysql이나 nosql등등 저장 방식이 바뀌어졌을때 아래와 같이 수정만 하면 돌아가고, 이것이 스프링의 장점입니다.

변경 할때 다른 파일은 건드리지 않고, 한파일의 몇줄만 수정하면 서비스가 돌아갑니다.

 

데이터 베이스를 다루는 JDBC 레포지토리로 바뀐다고 가정하겠습니다.

MemoryMemberRepository -> JdbcMemberRepository

package example.boot;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import example.boot.repository.JdbcMemberRepository;
import example.boot.repository.MemberRepository;
// import example.boot.repository.MemoryMemberRepository;
import example.boot.service.MemberService;


@Configuration
public class SpringConfig {
    
    private final DataSource dataSource;
    public SpringConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

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

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

위와 같이 수정해서 빈을 바로 변경할 수 있습니다.

반응형

댓글