프레임워크/Spring,Springboot

[Spring/Springboot] 스프링 bean과 등록 방법

Emil :) 2022. 10. 23. 18:49
728x90
반응형

목차

이 글은 Notion에서 작성 후 재편집한 포스트입니다.


서론


스프링 컨테이너 내부에서 서로 어떤 역할을 하는지는 대부분의 @어노테이션으로 해결이 가능합니다.
대표적으로 MVC모델에선 @Controller, @Service, @Repository 등의 어노테이션이 각각 컨트롤러, 서비스, 레파지토리를 나타내는 것으로 사용됩니다.

본 포스트에선 스프링 bean을 어떻게 등록하는지와 그 원리에 대해 알아보도록 하겠습니다.
김영한님의 인프런 스프링 강의 내용을 정리한 내용입니다.

참고


https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

 

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

 

본론


스프링 bean이란?


스프링하면 계~속해서 나오는게 빈(bean) 입니다.
구글링해보고 정의가 뭔지 알아보고 하면 복잡하게들 설명해놨던데, 간단하게 한마디로 축약하자면
스프링에서 관리당하는 자바 객체를 일컫습니다.

추가로, 이러한 빈들을 관리해주는 것이 빈 컨테이너(Bean Container) 입니다.

스프링 Bean 등록하기


앞서 말한대로, 스프링 IOC 에서 이 빈이라는놈을 어떻게 인식시킬것인가? 가 주된 내용입니다. 그 방법으론 여러가지가 있겠지만, 대표적으로 두가지를 활용합니다.

참고로, 스프링에서 스프링컨테이너에 빈을 등록할 땐 싱글톤 패턴으로 등록됩니다.

  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바코드로 직접 스프린 빈 등록하기

컴포넌트 스캔과 자동 의존관계 설정

간단합니다. 앞서말한 어노테이션을 활용하는 방식입니다.
그러나 Controller, Service 등을 할땐 @Controller, @Service를 활용하지 @Component를 활용하진 않습니다.
그런데 어떻게 빈 인식이 되는가?를 알기위해선 @Service라는 어노테이션을 뜯어볼 필요가 있습니다.

/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;

/**
 * Indicates that an annotated class is a "Service", originally defined by Domain-Driven
 * Design (Evans, 2003) as "an operation offered as an interface that stands alone in the
 * model, with no encapsulated state."
 *
 * <p>May also indicate that a class is a "Business Service Facade" (in the Core J2EE
 * patterns sense), or something similar. This annotation is a general-purpose stereotype
 * and individual teams may narrow their semantics and use as appropriate.
 *
 * <p>This annotation serves as a specialization of {@link Component @Component},
 * allowing for implementation classes to be autodetected through classpath scanning.
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see Component
 * @see Repository
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

 @Component 어노테이션이 붙어있는것을 확인할 수 있습니다.

 그중에서도
This annotation serves as a specialization of @Component, allowing for implementation classes to be autodetected through classpath scanning.
라는 문구가 있네요. 대충 "이 어노테이션은 컴포넌트에 특수화된 케이스다" 라고 써있네요.

@Controller와 @Repository도 마찬가지로 @Component가 붙어있는 것을 확인할 수 있습니다.

@Controller

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

@Repository

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

스프링은 @Component 라는 어노테이션이 달린 객체가 있으면, 스프링 컨테이너에서 모두 객체로 생성을 하게됩니다.
따라서 Controller, Service, Repository 등등이 이렇게 등록이 된 후에, @Autowired 어노테이션으로 아래 사진과 같이  Controller, Service, Repository 간의 연관관계를 설정해주게 됩니다.

출처 : 김영한님 스프링 입문 강의자료

 

자바 코드로 직접 스프링 빈 등록하기

앞서 말한대로 정리하면 다음과 같습니다.

  • 스프링 컨테이너는 @Component 가 붙은 객체를 스프링 빈으로 자동 등록한다.
  • @Controller, @Service, @Repository 가 스프링 빈으로 등록되는 이유는 내부에 @Component 가 있기 때문이다.

이러한 어노테이션 없이 직접 @Bean 으로 등록하는 방법 또한 존재합니다. SpringConfig 클래스를 service 패키지 안에 생성해줍니다.
간단하니 코드로 대체하도록 하겠습니다.

package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {
 @Bean
 public MemberService memberService() {
 return new MemberService(memberRepository());
 }
 @Bean
 public MemberRepository memberRepository() {
return new MemoryMemberRepository();
 }
}

 

@Component 스캔의 대상범위


그렇다면 @Component 가 붙은 파일이라면 어디서든 스캔이 되는걸까?

정답은 '아니오' 입니다.

패키지 구조는 다음과 같이 되어있다고 가정해봅시다. 그렇다면 현재 HellopSpringApplication이 존재하는 hello.hellospring 패키지 내부에 한해서 @Component를 가져오는 겁니다.

 

어떤게 더 효율적인가?


단편적으로 보면, 컴포넌트 스캔이 당연히 사용하기가 편합니다. 어노테이션 하나만 걸어주면 되니까요.
하지만 두 방법 모두 사용처가 명확히 있기 때문에, 사용방법을 알아둬야 합니다

주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용합니다.
반대로 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록합니다.

이게 무슨말이냐면, 만약 패키지 구조를 변경해야 하는데, 위 코드에서 MemoryMemberRepository를 참조하지 않고, DBMemberRepository 구현체를 참조하고 싶다고 한다면...
컴포넌트 스캔 방식을 사용하면 특정 객체를 사용하거나 호출하는 모든 파일을 변경시켜줘야 합니다.

하지만 이런 작업이 필요한 경우, 2번째 방법인 SpringConfig을 활용하면 다음과 같이 아주 단순하게 설정변경이 가능합니다.

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

// 위 코드에서 아래 코드로

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

 이처럼 상황별로 적용이 필요하니, 두 방법 모두 체득해놓는 것이 좋을 것 같습니다.

 

결론


스프링 빈이 등록되는 원리에 대해서 학습해봤습니다. 내용을 정리하자면 다음과 같습니다.

  • @Component가 존재하는 어노테이션이라면, 컴포넌트 스캔을 활용해 자동으로 스프링 컨테이너에 스프링 빈으로 등록이 된다.
  • 다만, @Component의 범위는 실행대산 application의 패키지에 한해서만 탐색 및 등록이 된다.
  • 어노테이션을 활용하지 않은 자바 직접 코드 등록은 @Bean으로 등록해주면 된다.
  • 컴포넌트 스캔과 직접 등록 방식이 존재한다. 각각의 활용처는 다음과 같다.
    • 컴포넌스 스캔 - 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드
    • 설정을 통한 자바 코드 구현 - 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하는 경우

구독 및 하트는 정보 포스팅 제작에 큰 힘이됩니다♡

728x90
반응형

'프레임워크 > Spring,Springboot' 카테고리의 다른 글

[Spring Batch] @JobScope, @StepScope  (0) 2023.11.15