본문 바로가기
Programming/Spring

[Spring] 의존성 주입 관련 어노테이션 정리

Spring Bean의 원리 

"개발자가 직접 객체 생성이 아닌 Spring이 대신 객체를 생성한다" 

Spring Bean은 Spring이 대신 객체를 생성 및 주입해주는 단위를 의미한다. 이때, Spring의 Application Context가 싱글톤 객체를 대신 만들어서 주입해준다. 

 

빈을 싱글톤으로 생성하는 이유 

스프링이 주로 적용되는 대상이 자바 엔터프라이즈 기술을 사용하는 서버환경이기 때문에 스프링은 빈을 싱글톤으로 만든다. 대규모 엔터프라이즈 서버 환경은 서버 하나당 최대로 초당 수십, 수백번씩 브라우저나 여러 시스템으로부터 요청을 받아 처리할 수 있는 높은 성능이 요구되는 환경이다. 또한, Spring MVC 구조에 따라 하나의 요청에 대한 응답을 만들기까지 Data Access, Service 등 다양한 기능을 담당하는 객체들이 모두 참여하는 계층형 구조이기 때문이다. 각 로직을 담당하는 객체를 새로 만들어서 사용하면 서버에 엄청난 부하가 올지도?

 

과거 Spring에서는 필요 객체들을 개발자가 개발한 뒤, XML을 통해 일일히 Bean에 등록했다. 현재 Spring에서는 XML이 아닌 어노테이션 지정으로 간편하게 Bean을 등록할 수 있다.

 

    <!-- Bean 정의 -->
    <bean id="myService" class="com.example.MyService">
        <!-- 생성자 주입 -->
        <constructor-arg value="Some Value"/>
        
        <!-- 프로퍼티 주입 -->
        <property name="someProperty" value="Another Value"/>
        
        <!-- 의존성 주입 -->
        <property name="anotherBean" ref="anotherBean"/>
    </bean>

    <bean id="anotherBean" class="com.example.AnotherBean">
        <!-- 프로퍼티 주입 -->
        <property name="property" value="Value"/>
    </bean>

 

스프링에서는 객체들을 관리하기 위해 제어의 역전(외부에서 관리하는 객체를 가져와 사용함)을 사용한다. 그리고 제어의 역전을 구현하기 위해 사용하는 방법이 의존성 주입(DI)이다. DI는 어떤 클래스가 다른 클래스에 의존한다는 것이다. 

public class A{
  @Autowired
  B b;
}

 


스프링은 스프링 컨테이너를 제공한다. 스프링 컨테이너는 빈을 생성하고 관리하는데, 빈이 생성되고 소멸되기까지의 생명 주기를 스프링 컨테이너가 관리한다. 그래서 @Autowired 같은 어노테이션을 사용해 빈을 주입 받을 수 있게 DI를 지원한다. Bean은 아래에서 확인하겠다. 

 

위 예제에서는 스프링 컨테이너에서 B 객체를 만들어 클래스 A에게 주었다. 즉, 객체를 직접 생성한 것이 아니라 스프링 컨테이너에서 객체를 만들어 주입해 준 것이다. 

 


Spring Bean(어노테이션 아님)

스프링 컨테이너가 생성하고 관리하는 객체. 애플리케이션 전역에서 재사용될 수 있다. 

예를 들어, MyBean이라는 클래스에 @Component 어노테이션을 붙이면 MyBean 클래스가 빈으로 등록된다. 이후 스프링 컨테이너에서 이 클래스의 Life Cycle을 관리한다. 이때 빈의 이름은 클래스 이름의 첫 글자를 소문자로 바꿔 관리한다. 

 

@Component

Spring이 해당 클래스를 빈으로 등록한다. Bean과 마찬가지로 애플리케이션의 다른 구성 요소에서 자동으로 주입 할 수 있다. 다른 클래스에서 @Autowired와 같은 어노테이션을 사용해서 이 빈을 주입 받을 수 있다. 
@Component의 특별화 된 형태로 Service, Repository, Controller가 있다. @SpringApplication이 동작할 때 모든 @Component를 스캔한다. (@ComponentScan)

 


근데 Bean을 등록하면 좋은 점이 무엇일까

 

애플리케이션의 구조와 유지 보수를 효율적으로 하는데 많은 장점이 있다. 또한, 이를 통해 클래스 간 의존성을 명시적으로 관리하지 않아도 된다. 객체의 생성과 초기화가 Spring 컨테이너에 의해 자동으로 관리되기 때문이다. 

빈의 생성, 초기화, 소멸 등의 과정이 자동으로 처리되므로 개발자는 생명 주기 관련 코드를 직접 작성할 필요가 없다. 

 

AOP(Aspect-Oriented Programming)

관심사의 분리. Spring AOP를 사용하면 비지니스 로직과 공통 관심사를 분리할 수 있다. 빈으로 등록된 객체에 AOP 기능을 적용해 유지보수 하기 쉬운 코드를 작성할 수 있다. 

 

 

@Bean과 @Component? 

@Bean은 메서드 레벨에서 사용된다. 메서드가 반환하는 객체를 Spring 애플리케이션 컨텍스트에 빈으로 등록할 때 사용된다. 주로 @Configuration 클래스를 사용해 빈을 등록할 때 사용된다. 

빈의 생성과 초기화를 명시적으로 제어할 수 있다. 반환되는 객체를 개발자가 수동으로 빈으로 등록한다. 

 

Component는 클래스 레벨에서 사용된다. 스프링이 컴포넌트 스캔을 통해 자동으로 빈을 찾고 등록한다. 일반적으로 빈 등록을 위한 기본 방법이며 다양한 계층에서 사용하는 클래스를 자동으로 빈으로 등록할 때 사용한다. 

 

Bean은 개발자가 직접 컨트롤 할 수 없는 경우, Component는 개발자가 직접 컨트롤 할 수 있는 클래스에 사용한다. 

 

서드파티 클래스도 적용할 수 있다

개발자가 직접 작성하지 않고 외부에서 제공되는 클래스를 의미한다. @Bean을 사용해서 서드파티 라이브러리 클래스도 Spring Bean으로 등록할 수 있다. (서드파티 클래스는 @Component를 사용할 수 없다.) 

 

또한 인스턴스 선택 로직의 도입도 가능하다. 어떤 인스턴스 옵션을 사용할 지 결정할 수도 있다. 때문에 @Bean이 좀 더 복잡하고 유연한 의존성 주입을 활용할 수 있다. 빈을 생성하기 전에 다른 설정이나 초기화 작업도 수행할 수 있다. 

(예컨대 URL 설정 등) 

 

반면 Component는 단순히 스캔하고 등록하는 용도로 사용되므로 추가적인 로직이 필요 없는 경우가 적합한 것이다.

 

 

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        // 객체 생성 로직을 이곳에 정의
        return new MyService();
    }
}

 

위 예시의 경우 MyService를 생성하는 주체가 Spring이 아니다. AppConfig 클래스의 myService() 메서드가 MyService 객체의 생성 및 빈 등록을 책임진다. Spring에 의존하지 않고 POJO(Plain Old Java Object)로 존재할 수 있다. 

 

 

 

 

Spring 사용(주입) 방법 

1. 생성자 주입(Constructor Injection) 

실제 현업에서 가장 많이 사용하는 방법이다. 순환 참조 여부를 객체 생성 후가 아닌 생성 전에 인지해 실수를 방지할 수 있으며 final을 사용해 Bean 객체의 불변성을 보장한다. 때문에 프로그램이 돌아가던 중 순환 참조를 만나 터지는 일이 없게 만든다. 그래서 Spring에서 가장 추천하는 방식이다. 

 

@RequiredArgsConstructor

Lombok 라이브러리의 어노테이션이다. 초기화 되지 않은 final 필드에 대해 생성자를 생성해준다. 새로운 필드를 추가할 때 다시 생성자를 만들어서 관리해야 하는 번거로움을 없애준다. 생성자의 매개 변수는 필드의 선언 순서와 동일한 순서로 배치 된다. 

public class Example {
    private final int value;
    private final String name;
    private final boolean active;

    public Example(int value, String name, boolean active) {
        this.value = value;
        this.name = name;
        this.active = active;
    }
}
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class Example {
    private final int value;
    private final String name;
    private final boolean active;
}

 

 

2. 필드 주입(Property Injection) 

주니어 개발자들이 가장 많이 선호하는 방식이라고 한다. 나도 처음에 Spring 배웠을 때 무조건 Autowired를 붙였던 기억이 있다. 필드마다 Autowried가 명시되어 있어 직관적이라고 생각했던 것 같다. 

Spring이 자동으로 필요한 의존성을 주입해준다. 필드, 생성자, 메서드에 적용될 수 있다. ComponentScan에 의해 관리되는 Bean에 의존성을 주입한다. 

class MyClass {
		@Autowired
		private MyService service;
	
		public void test() {
				service.test()
		}
}

 

 

3. 수정자 주입(Setter Method Injection) 

class MyClass {
		private MyService service;
	
		@Autowired
		public void setService(MyService service) {
				this.service = service;
		}
}

 

 

@Configuration

하나 이상의 @Bean 메서드를 포함하고 있음을 나타낸다. Spring 컨테이너가 해당 클래스를 Bean 정의로 사용해야 함을 명시한다.

Bean을 수동으로 등록하기 위해서는 설정 class 위에 @Confguration을 추가한다. @Configuration 내부를 보면, 사실 @Component가 추가되어있다. 따라서 해당 class 또한 Bean인 것이다. 

단순하게 @Bean만 사용했을 때는 싱글톤 보장이 안 된다. 특정 메서드를 호출할 때마다 새로운 Bean을 생성하기 때문에 호출한만큼 객체가 생성된다. 하지만 @Configuration 클래스 아래 메서드에 @Bean을 적용하면 싱글톤이 유지된다. 

 

 

 

 

 

 

 

Reference

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html

 

Configuration (Spring Framework 6.1.11 API)

Specify whether @Bean methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct @Bean method calls in user code. This feature requires method interception, implemented thro

docs.spring.io

 

https://blogshine.tistory.com/551

 

[Spring] @Configuration 이란?

이번 글에서는 @Configuration을 사용하는 이점에 대하여 정리해볼까 한다. 1. @Configuration 이란? " data-ke-type="html"> HTML 삽입 미리보기할 수 없는 소스 Spring에서 Bean을 수동으로 등록하기 위해서는, 설

blogshine.tistory.com

 

https://velog.io/@power0080/SpringRequiredArgsConstructor%EC%99%80-%EC%88%9C%ED%99%98-%EC%B0%B8%EC%A1%B0

 

[Spring]@RequiredArgsConstructor와 순환 참조

취업하기 전 학원에서 공부한 코드와 혼자 작성해둔 코드를 가볍게 훑어보던 중 제 눈에 확 띄게 들어온 녀석이 있었습니다. 저의 시선을 한 번에 강탈해버린 매혹적인 녀석 @Autowired , 이 녀석을

velog.io

 

[도서] 스프링 부트 3 백엔드 개발자 되기: 자바 편

 

'Programming > Spring' 카테고리의 다른 글

[JPA] QueryDSL  (1) 2024.06.19
[Spring] Spring DI (Chapter 3) - 1  (0) 2024.02.12
[Spring] Spring을 시작하기(Chapter 2)  (0) 2024.02.10
[Spring] Spring을 시작하기 위한 환경 설정(Chapter 1)  (0) 2024.02.03
[Spring] Transaction  (0) 2023.06.16