DEV/Spring

[Spring] @Autowired 활용 의존성 주입, Spring Bean (2 / 2)

Bi3a 2023. 11. 16. 21:49

728x90

[Spring] @Autowired 활용 의존성 주입, Spring Bean 에 대해 알아봅시다.
Spring boot 기초 깨부시기

 

이전 포스팅에서 이어집니다.

 

의존성 주입(Dependency Injection) (1 / 2)

의존성 주입 객체가 필요로 하는 어떤 것을 외부에서 전달해주는 행위를 말하며, 포괄적인 의미로서 두 객체 간의 관계를 외부에서 결정해주는 디자인 패턴을 의미합니다. 예시(의존성 주입이

doinitright.tistory.com

 


@Autowired

다양한 타입의 의존성을 자동으로 처리해 줄 수 있는 Spring 프레임워크 지원하는 어노테이션입니다.

 

@Autowired는 "인터페이스를 선언한 변수에 자동적으로 그 인터페이스를 재정의한 객체를 주입시킨다"는 의미의 어노테이션입니다.

그러나, @Autowired는 생성자, Setter, 필드 등에 사용될 수 있으므로 이보다는 더 쉬운 의미로 접근하고자 한다면

"@Autowired는 의존성이 있는 객체 ↔ 객체를 매칭시켜준다" 라고 이해하시면 편합니다.

그 과정에서 전제 조건은 두 가지가 있습니다.

  1. @Autowired를 사용하는 (생성자, Setter, 필드)를 내부에 가지고 있는 객체가 스프링 빈에 등록되어 있어야 하는 것
  2. @Autowired를 사용해 (생성자, Setter, 필드)를 통해 주입시킬 인자 객체가 스프링 빈에 등록되어 있어야 하는 것

 

스프링 빈(Spring Bean)?
스프링 차원에서 관리하는 객체로, "공유 객체" 의 의미와 근접합니다.
Bean은 스프링 IOC 컨테이너에 의해 재사용 가능한 자바 객체입니다.

일반적인 자바 객체의 경우 앱 실행 내에서 사용이 중단될 시 일정 흐름에 따라 가비지 컬렉션에 의해 청소됩니다.
그러나, 스프링 빈에 등재된 객체인 경우 그 가치 존재를 설계자가 인정함으로써 재사용 가능한 형태로 대기시키며, 호출될 시 필요에 의해 이를 주입시킵니다.

스프링 빈으로 클래스를 등재하는 방법은 클래스 상단에 @Component, @Service, @Repository, @Service 등 중 하나를 명시하는 것입니다.

해당 어노테이션 중 하나를 선언하면 앱 구동 시  스프링이 클래스의 객체를 자동 생성해 Bean 객체로 관리합니다.

 

기존 예시를 활용한 @Autowired 이해

class Eat {
    private final Meat meat;
    
    // 생성자 주입
    public Eat(Meat meat){
    	this.meat = meat
    }
    
class Meat{}
class Beef extends Meat{}
class Pork extends Meat{}

저번 포스팅에서 생성자를 활용한 의존성 주입에 대해 알아봤고, 위가 해당 예시입니다.

Eat 클래스는 Meat을 생성자를 통해 의존성 주입을 받고 있습니다. 

@AutoWired를 통해 고유한 Meat 객체 meat를 자동적으로 Eat 클래스의 의존성으로 주입을 시키기 위해서는

앞서 말한 두가지 전제조건이 필요합니다. 바로, Meat과 Eat을 스프링 빈 객체로 등록시키는 것입니다.

 

@Component
class Eat {
    private final Meat meat;
    
    // 생성자 주입
    public Eat(Meat meat){
    	this.meat = meat
}
@Component
class Meat {}

위 과정을 거칠 시 스프링 IOC 컨테이너에서는 두 클래스로 인해 생성되는 객체를 프로그램 실행 시 자동으로 스프링 빈의 고유 객체로 인식하고 저장하게 됩니다. 

그 이후, 자동적으로 Eat 객체에 주입시킬 Meat 객체를 스프링 빈 객체 중에서 자동으로 찾아서 이를 연결시켜 주기 위한 과정에서 @Autowired가 사용됩니다.

 

@Component
class Eat {
    private final Meat meat;
    
    @Autowired
    public Eat(Meat meat){
    	this.meat = meat
}

위와 같이 생성자에  @Autowired를 붙이게 되면,

@Component 등을 붙여 스프링 빈 객체로 관리하고 있는 Eat 객체와 Meat 객체를 자동으로 찾아 이를 연결시켜 줍니다.

 

동일한 타입의 빈 객체가 여러 개면? → @Qualifier를 사용한다

@Component
class Meat {}

private final Meat Beef = new Meat();
private final Meat Pork = new Meat();
private final Meat Chicken = new Meat();

이 과정에서 의문점이 생길 수 있습니다. 

"그렇다면 동일한 타입인 Meat 객체가 여러개 생긴다면 @AutoWired를 통해 자동으로 연결되는 객체는 무엇일까?"

이런 문제를 해결하기 위해 @Qualifier 어노테이션을 사용할 수 있습니다.

@Qualifier (식별자)
사용할 의존 객체를 선택할 수 있도록 해주는 어노테이션입니다.
@Autowired로 인해 적용된 주입 대상에 설정함으로써 주입할 빈 객체를 지정할 수 있습니다.

 

@Component
class Eat {
    private final Meat meat;
    
    @Autowired
    public Eat(@Qualifier("chicken") Meat meat){
    	this.meat = meat
}

위는 @Qualifier를 활용해 주입 대상 Meat 객체를 chicken으로 설정하는 예제입니다.

 @Qualifier를 통해 주입 대상을 설정해 줌으로써, 빈 객체를 주입하는 과정에서 객체 간의 충돌을 방지할 수 있습니다.

만약 각 타입의 빈 객체가 하나만 존재한다면,

@Qualifier를 통한 식별자 설정 없이도 @Autowired 만을 사용해도 자동으로 연결이 됩니다.

 

생성자 주입 시 @Autowired를 생략해도 되는 경우

@Component
@RequiredArgsConstructor
class Eat {
    private final Meat meat;

    // @Autowired
    // public Eat(Meat meat){
    // this.meat = meat
// }

그거 알고 계신가요? @RequiredArgsConstructor를 사용하면 생성자가 생성이 됩니다.

Spring IOC 컨테이너는 Bean을 통해 등록되어 있는 두 객체가 생성자 주입을 통해 의존 관계가 성립이 될 시,

 @Autowired를 생략해도 @Autowired처럼 의존성을 주입해 주는 기능을 제공합니다.

이를 "암시적 생성자 주입 기능"이라고 정의합니다.

 

@RequiredArgsConstructor를 사용하면  이 "암시적 생성자 주입 기능"으로 인해 @Autowired가 작동하게 됩니다.

암시적 생성자 주입(Implicit Constructor Injection)

생성자가 하나뿐이고 해당 생성자의 매개변수가 모두 빈으로 등록되어 있으면 암시적으로 생성자가 주입됩니다.
(@Autoired가 생략되어 있어도 @Autowired가 작동된다는 뜻)

 

@Component
@RequiredArgsConstructor
class Eat {
    private final Meat meat;
    private final Veg veg;
    
    // @Autowired
    // public Eat(Meat meat, Veg veg){
    	// this.meat = meat;
        // this.veg = veg;
// }

@Component
class Veg {}

@Component
class Meat{}

위 예시 같은 경우도 Veg와 Meat 클래스가 모두 빈 객체로 선언되어 있기 때문에,

@Autowired를 생략해도 자동적으로 의존성 주입이 동작하겠습니다.

 

@Autowired를 활용한 Setter 주입과 필드 주입 예시

// Eat 클래스 with 세터 주입
@Component
public class Eat {
    private final Meat meat;

    @Autowired
    public void setMeat(Meat meat) {
        this.meat = meat;
    }
}

Setter 주입

// Eat 클래스 with 필드 주입
@Component
public class Eat {
    @Autowired
    private final Meat meat;
}

필드 주입

 


@Autowired에 대해 알아보았습니다.