BEAN(IoC, 의존성)

마자나다·2023년 12월 5일

Spring

목록 보기
2/9

보충 설명 블로그 정리 글: https://velog.io/@guscjf0903/Bean-추가-내용

Bean은 자바 Bean과 스프링 Bean으로 나뉜다.

자바 BEAN

자바 BEAN이란?

  • 자바 빈은 자바 언어의 표준 명세서를 따르는 일반적인 자바 클래스를 가르키는 용어이다.
  • 자바 빈은 데이터를 캡슐화하고 재사용 가능한 소프트웨어 컴포넌트를 개발하기 위해 사용된다.

자바 Bean의 규약

  1. public 클래스 : 자바 빈은 'public'으로 선언된 클래스여야한다.
  2. 기본 생성자 : 자바 빈은 파라미터가 없는 기본생성자를 가지고 있어야 한다. 기본 생성자는 명시적으로 정의되거나 컴파일러에 의해 자동으로 생성될 수 있다.
  3. 속성(Properties) : 자바 빈은 속성을 표현하기 위해 필드를 사용한다. 이러한 필드는 'Private'접근제어자로 선언되며, 데이터의 캡슐화를 하는데 사용된다.
  4. Getter, Setter : 각 속성에 대한 게터와 세터 메서드를 제공한다. 게터는 속상값을 변환하는 메서드이고, 세터는 속성 값을 설정하는 메서드이다.
  5. 프로퍼티가 boolean이면 get이 아니라 is라고 사용해도 된다.
public class PersonBean implements Serializable {
    private String name;
    private int age;

    // 기본 생성자
    public PersonBean() {
    }

    // 프로퍼티의 getter 메서드
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // 프로퍼티의 setter 메서드
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

스프링 BEAN

스프링 BEAN이란?

  • Bean은 스프링 IoC컨테이너에 의해 관리되는 재사용가능한 자바 객체이다.
  • XML, Annotation, Java config 파일을 기반으로 한 Configuration 메타데이터와 Bean으로 등록할려는 POJO를 통해 Bean을 생성할 수 있다. (예전엔 어노테이션이 없어서 XML밖에 쓸게 없었다. 지금은 어노테이션으로 거의다 처리한다.)
    업로드중..
  • Configuration 메타데이터 : XML, 자바코드, 어노테이션으로 표현되며, 컨테이너의 명령과 인스턴스화, 설정, 조립할 객체등을 정의한다.
  • 사진에서 처럼 Configuration과 POJO가 합쳐져 Bean을 생성하고 다양한 기능들을 설정한다.

Dependency Injection

  • 의존성(Dependency)이란 말 그대로 하나의 코드가 다른 코드에 의존하는 상태를 뜻한다. 업로드중..
  • 의존성 주입(Dependency Injection)이란 의존성이 있는 코드 객체를 넣어준다는 뜻이다.
  • 사진에서처럼 A라는 코드가 B라는 코드를 만들지않고, 인스턴스를 생성해서 사용하고 있다면, A는 B에 의존하고 있다고 말할 수 있다.
    업로드중..
  • 의존성 주입이 계속해서 일어난다면 어디선가 new 키워드가 사영되여 객체를 생성한다.
  • 그럼 위 사진과 같이 하나의 객체를 만들기 위해 굉장히 복잡하게 얽혀 의존성이 생성될 수 있다. 또한 많은 객ㅊ레가 중복생성되게 된다. 이같은 문제를 해결하기 위해 스플이 IoC컨테이너를 사용한다.

IoC 컨테이너란?

  • IoC란 Inversion Of Control의 약자로 제어의 역전이다.
  • 프로그램의 실행흐름, 객체의 생명주기를 개발자가 아닌 외부에서 관리해준다.
  • Spring이 직접 자바 객체를 생성하고 관리하기 때문에 이 관리 위임의 주체는 Spring이 된다.
    업로드중..
  • 그냥 의존성 주입을 하게 되면 사진의 '일반적인 경우'처럼 일직선으로 이어짐과 달리 중간의 매개체가 끼어들어 더이상 제어를 개발자가 아닌 매개체(=IoC 컨테이너)가 일임하게 된다.
  • IoC컨테이너는 의존성 관리 뿐만아닌, 인스턴스 주입, 더나아가 메모리 해제의 역할까지 해준다.
  • 떄문에 의존성 주입이 필요한 객체를 Bean으로 등록하여 스프링 IoC컨테이너가 생성과 의존성 주입을 관리하도록 해야한다.

Bean으로 등록하는 방법

하나의 예시를 두고 다양한 방법을 알아보자

  • BookService.java 파일을 Bean으로 등록해보자.
public class BookRepository {
}
public class BookService {

    BookRepository bookRepository;

    public void setBookRepository(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

}

1.xml-bean 태그 이용

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <bean id="bookService"
          class="com.springstudy.springapplicationcontext.BookService">
        <property name="bookRepository" ref="bookRepository"/>
        <!--name="bookRepository" 이부분에서 bookRepository는 bookService의 setter에서 가져옴-->
        <!--ref="bookRepository" 이부분에서 bookRepository는 bean id 에서 가져옴-->
    </bean>

    <bean id="bookRepository"
          class="com.springstudy.springapplicationcontext.BookRepository">
    </bean>
</beans>
  • id는 bean의 이름, class는 빈을 등록하고자하는 클래스가 위치한 패키지명이 포함된 위치
  • 하지만 xml을 이용한 의존성 주입은 현재 거의 사용되지 않는다.
  1. java Configuration 사용
@Configuration // bean을 등록하는 Configuration 이다.
public class ApplicationConfig {

   @Bean
   public BookRepository bookRepository() { //메소드의 이름이 Bean의 이름
       return new BookRepository();
   }

   @Bean
   public BookService bookService() {
       BookService bookService = new BookService();
//        아래와 같이 직접 의존성을 주입하지 않아도 @Autowired를 통하여 주입할 수 있다.
//        하지만 BookService에 setter메서드가 있어야 가능하다 !
//        bookService.setBookRepository(bookRepository());
       return bookService;
   }

}
  • @Configuration이 부으면 자바 클래스 파일을 자바 설정파일로 등록할 수 있다.
  • @Bean 어노테이션으로 빈으로 등록할 수 있다.
  • 이 방법 역시 xml 파일과 마찬가지로 잘 쓰이지 않는다. 수동으로 Bean을 등록해줘야 하기 떄문에 생산성이 많이 떨어진다.
  • 하지만 외부에서 만든 라이브러리 등록은 이 방법으로 사용된다.
  1. ComponentScan 사용
    AppConfig.java파일 생성 및 Bean을 생성한다.
@Configuration // bean을 등록하는 Configuration 이다.
//@ComponentScan(basePackages = "com.springstudy.springapplicationcontext")
//위의 방법은 type safety 하지 않으므로 아래의 방법을 추천한다.
@ComponentScan(basePackageClasses = DemoApplication.class)
// DemoApplication 클래스가 위치한 곳부터 scan
public class ApplicationConfig {

}
  • 컴포넌트 스캔은 컴포넌트 어노테이션을 가진 모든 대상을 가져와서 빈을 등록하기 위해 찾는 과정.
  • 베이스 패키지,베이스 패키지 클래스 라는 속성을 이용해서 탐색하는 위차를 지정해 줄 수 있다.
  • basePackages : 해당 패키지에 속한 컴포넌트를 스캔한다.
  • basePackageClasses : 해당 클래스가 속한 패키지를 스캔한다.

IoC 컨테이너에 의해 빈으로 등록되는 대상 어노테이션
1. @Configuration : 자바 기반의 설정 클래스를 나타낸다. 메서드에 @Bean어노테이션을 사용해서 빈에 등록할 수 있다.
2. @Component : 붙은 모든 클래스들을 자동으로 빈에 등록해준다.
3. @Controller : 웹 어플리케이션의 컨트롤러 클래스로 사용된다. 스프링은 이 어노테이선이 붙은 클래스를 컨트롤러로 인식하고 URL을 요청 처리한다.
4. @Service : 서비스 클래스로 사용된다. 스프링 비즈니스 로직에서 사용된다.
5. @Repository : 데이터 액세스 객체(DAO)로 사용된다. 스프링 데이터 접근 계층에서 사용된다.
6. @Autowired : 의존성 주입을 위해 사용되는 어노테이션이다 해당 필드또는 메서드에 필요한 빈을 자동으로 주입시켜준다.

BEAN과 싱글톤 패턴

  • '빈은 싱글톤이다'라는 말이 있다. 만약 객체를 스프링이 아닌 직접 싱글톤으로 만들어 사용한다면 다양한 부작용이 있다.
    1. 다형성을 사용하지 못한다 : 싱글톤은 생성자의 접근제어자를 private으로 만들기 때문에 상속하지 못한다.
    2. 안정적인 어플리케이션에서 단위테스트가 어렵다. : 싱글톤 패턴으로 구현할 경우 해당 객체는 공유 객체가 되므로, 단위테스트 순서에 따라 결과가 달라질 수 있다.
  • 하지만 스프링은 이같은 단점들을 해결하면서 모든 빈을 싱글톤 객체로 사용한다.

쬐끔 디테일한 Bean 등록 과정(IoC 컨테이너의 라이플 사이클, 관리 방법)

  1. 객체 생성 + Property 설정 : 어노테이션, 자바 Config, XML 메타데이터를 사용하여 Bean Definition을 생성, Bean으로 등록할 POJO와 Bean Definition을 이용해서 Bean을 생성한다.
    ※ Bean Definition : 스프링 컨테이너에게 빈을 어떻게 생성하고, 설정해야 하는지에 대한 메타데이터를 제공
    위 과정에선 평범한 자바클래스를 사용하여 객체를 생성합니다.
  2. IoC 컨테이너, 싱글톤 레지스트리 :스프링 IoC 컨테이너 는 빈 스코프가 싱글톤인 객체에, 빈의 이름을 Key객체를 Value로 저장한다. 그래서 의존성이 주입 되어야하는 객체가 빈으로 등록 되어 있을 때, Key를 이용해서 항상 동일한 Single Object를 반환하게 된다.
    ※ 싱글톤 레지스트리 : 스프링 컨테이너가 관리하는 빈 중에서, 싱글톤 범위로 관리되는 빈들을 저장하고 관리하는 메커니즘.
    - 싱글톤 레지스트리 특징
    - 전역적인 접근 : 모든 빈을 전역적으로 관리한다. 따라서 어디서든지 동일한 싱글톤 빈에 접근 가능
    - 스프링 컨테이너가 빈 관리 : 컨테이너가 빈 라이프사이클을 관리하므로, 빈의 생성 및 소멸과 같은 관리작업을 사용자가 할 필요가 없다.
    - 싱글톤 패턴과 다름 : 일반적인 디자인패턴의 싱글톤과 다르다. 스프링의 싱글톤은 스프링 컨테이너 내에서만 적용되며, 컨테이너가 관리,생성을 하기 때문에 여러 스레드에서 안전하게 사용할 수 있다.
  3. 의존설정 : 빈 객체가 생성 된 뒤, IoC컨테이너는 의존설정을 한다. 이 때 의존성 자동 주입이 일어나게 된다.
  4. 초기화 -> 사용 -> 소멸 : 커넥션 풀처럼 초기화 과정이 필요한 객체들은 초기화를 진행하고, 빈을 사용자가 사용하게 된다. 그리고 스프링 컨테이너가 종료될 때 빈 스코프가 싱글톤이 객체도 함께 소멸된다.
요약
1. 설정과  POJO사용해서 객체를 생성한다.
2. IoC컨테이너가 빈 이름 key , 객체는 Value로 저장해서  싱글톤을 반환한다.
3. 의존설정하고 자동주입한다

Bean Scope

  • 빈 스코프란 빈 객체의 생명주기와 범위를 정의하는 것을 의미한다. 스코프가 언제 생성되고 소멸되며, 어떤 범위내에서 빈 객체를 공유할 것인지 결정한다.
  1. Singleton Scope(기본값)
    • 싱글톤 스코프는 컨테이너에서 단일 인스턴스를 생성하고, 어플리케이션 내에서 모든 요청에 동일한 인스턴스를 반환한다.
    • 하나의 Bean 객체가 모든 어플리케이션 전체에 공유된다
    • @Scope(”singleton”)또는 @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)로 설정한다.
    • 거의 싱글톤만 쓴다고 생각해도 무방하다. 그외에 것들은 거의 잘 쓰이지 않는다
  2. Prototype Scope
    • 프로토타입 스코프는 매번 빈을 요청할때마다 새로운 인스턴스를 생성한다.
    • 요청마다 별도의 빈 인스턴스가 생성되고, 각 빈 인스턴스는 독립적으로 관리된다.
    • @Scope(”prototype”)또는 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)로 설정한다.
  3. Request Scope
    • 요청 스코프는 웹 어플리케이션에서 HTTP 요청마다 새로운 인스턴스를 생성한다
    • 주로 웹 어플리케이션 개발에 사용, 각 HTTP요청마다 별도의 빈 인스턴스를 생성, 요청 범위 내에서 공유한다.
    • @Scope(”request”)로 설정된다.
  4. Session Scope
    • 세션 스코프는 웹 어플리케이션에서 세션마다 새로운 인스턴스를 생성
    • 각 세션마다 별도의 빈 인스턴스를 생성하여 세션범위 내에서 공유한다.
    • @Scope(”session”)로 설정된다.
  5. Application Scope(ServletContext Scope)
    • 어플리케이션 스코프는 웹 어플리케이션의 생명주기 동안 단일 인스턴스를 생성한다.
    • 웹 어플리케이션 내에 모든 요청및 세션에세 동일한 빈 인스턴스를 공유한다.
    • @Scope(”application”)로 설정된다.

Bean 의존성 주입 방법

  • 스프링에서 Bean 주입 방법은 다른 빈에게 빈 객체를 제공하고 의존성을 관리하는 프로세서를 나타낸다.
  • 스프링에서 빈 주입은 주로 의존성 주입을 통해 이루어진다.
  1. 필드 주입 방식
@Autowired private MemberRepository memberRepository;
  • 장점
    1. 직관적 필드에 @Autowired만 사용하면 되기 때문에 코드가 단순하다.
  • 단점
    1. 의존성이 외부에 변경할 수 없어서 테스트하기 어렵다. 다른 클래스와 교체하거나 모의 객체 주입이 어렵다.
    2. 의존성 주입을 외부에서 명시적으로 설정하기 어렵기 때문에 컴파일시 문제를 잡기 어렵다.
  1. Setter 주입 방식
private MemberRepository memberRepository;

public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}
  • 장점
    1. 객체를 생성 후에도 의존성을 변경할 수 있어서 유연성이 높다. 실행중에도 의존성 교체가 가능하다.
    1. Setter 메서드는 public으로 선언되어야 하므로 외부에서 임의로 수정될 위험이 있다. 또 중간에 의존성을 변경하는 경우가 드물기 때문에 너무 유연할 수 있다.
  1. 생성자 주입 방식
@Service
public class MemberService {
		private final MemberRepository memberRepository;
		
		@Autowired
		public MemberService(MemberRepository memberRepository) {
		    this.memberRepository = memberRepository;
		}
  • 장점
    1. 의존성을 불변하게 만들어서 객체 생성 이후에 변경할 수 없도록 한다. 이로써 의존성을 더 안정적으로 관리할 수 있다.
    2, 컴파일 시에 의존성 주입 오류를 쉽게 감지할 수 있다.
    1. 단위 테스트와 모의 객체를 사용한 테스트가 쉽고 효과적으로 수행된다.
  • 단점
    1. 코드가 다소 길어질 수 있다. 객체가 생성 될 때 의존성을 주입해야 하므로 생성자의 파라미터가 많아질 경우 코드의 가독성이 감소 할 수 있다.

번외. Lombok 사용

@Service
@RequiredArgsConstructor
public class UserSignupService {
    private final UserRepository userRepository;
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Transactional
    public void registerUser(SignupDTO signupDTO) {
        User user = toEntity(signupDTO);
        userRepository.save(user);
    }
}
  • 위 코드처럼 롬복의 @RequiredArgsConstructor 어노테이션을 사용해서 final필드로 되어있는 변수에 빈을 삽입한다.
  • 가장 코드가 깔끔하고, 자주사용되는 방식이다.

출처및 참고
https://velog.io/@ldb0820/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-BEAN%EB%93%B1%EB%A1%9D-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EC%8B%9D
https://www.youtube.com/watch?v=3gURJvJw_T4
https://seongmun-hong.github.io/spring/Spring-Bean-Create
https://engineerinsight.tistory.com/44
https://www.youtube.com/watch?v=UcpLNgko8lg
https://steady-coding.tistory.com/594

profile
우왕좌왕 개발

0개의 댓글