Framework/Spring

[Spring] Bean의 주입은 어떻게 하는게 좋을까?

KOOCCI 2022. 8. 14. 02:53

목표 : Spring Bean 생성자 주입이 많은 이유를 설명할 수 있다.


이전 포스팅으로 Bean 주입에 대해 설명한 적이 있다.

잠깐 다시 정리해보자.

 

Bean의 주입 방법

 

Bean의 주입 방법은 크게 3가지가 있다.

생성자 주입, Setter를 통한 주입은 앞선 포스팅으로 보았고,  필드를 통한 주입까지 3가지가 있다

 

Setter를 통한 주입

package com.example.demo.di;

import lombok.Data;

@Data
public class MyBean {
    private int msg;
}
package com.example.demo.di;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;

@Data
public class AnotherBean {
    private MyBean myBean;

    public void setMyBean(MyBean myBean) {
        this.myBean = myBean;
    }
}
package com.example.demo.di;

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

@Configuration
public class AppConfig {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

    @Bean
    public AnotherBean anotherBean() {
        return new AnotherBean();
    }
}
package com.example.demo.di;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        AnotherBean aBean = context.getBean(AnotherBean.class);
        MyBean myBean = context.getBean(MyBean.class);
        myBean.setMsg(123);
        aBean.setMyBean(myBean);

        System.out.println(aBean.getMyBean().getMsg());
    }
}

Setter를 통한 주입은 잘 사용하지 않는다.

그 이유는 Setter 자체에 있다.

Setter를 사용하는 것이 자유롭다보니, 나중에 누군가에 의해 변경이 일어날 수 있다는 것이다.

즉, 객체의 불변성을 보장하지 못한다.

 

그리고, 순환 참조에 대한 문제도 가지고 있다.

A도 B를 가지고 있고, B도 A를 가지고 있으면 순환 참조로 인해 에러가 날 것이다.

Setter 로 인해, Bean을 생성한 후에 주입하기 때문이다.

필드를 통한 주입

위 예시에서 AnotherBean만 바꾸어 주자.

@Data
public class AnotherBean {
    @Autowired
    private MyBean myBean;
}

간단히, @Autowired를 통해 주입해줄 수 있다.

최근 프로젝트가 아니라면, 이런 방식이 정말 많이 쓰였고, 또 자주 보일 것이다.

사용하기 편리하지만 이 역시 Setter와 동일하게 순환참조객체의 불변성에 대한 보장이 어렵다.

 

생성자를 통한 주입

 

이 글의 목적이기도 하다.

package com.example.demo.di;

import lombok.Data;
import lombok.RequiredArgsConstructor;

@Data
@RequiredArgsConstructor
public class AnotherBean {
    final private MyBean myBean;
}
package com.example.demo.di;

import lombok.Data;

@Data
public class MyBean {
    private int msg;
}
package com.example.demo.di;

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

@Configuration
public class AppConfig {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

    @Bean
    public AnotherBean anotherBean() {
        return new AnotherBean(this.myBean());
    }
}
package com.example.demo.di;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        AnotherBean aBean = context.getBean(AnotherBean.class);
        aBean.getMyBean().setMsg(123);

        System.out.println(aBean.getMyBean().getMsg());
    }
}

달라진 점은 크게 2가지다.

@RequiredArgsConstructor 를 통해, 생성자를 만들어준 점, 그리고, 필드에 final을 설정한 점이다.

 

생성자를 통한 주입을 Spring에서도 추천하는 이유는 순환참조 방지객체의 불변성을 보장해주기 때문이다.

앞서, 필드나 Setter를 통한 주입과 달리, 주입하고자 하는 Bean이 있는지 확인하면서 주입이 되므로 순환참조가 생길 수 없다.

 

또한, final 키워드를 유일하게 쓸 수 있기 때문에, 런타임에 객체가 변환되는 것에 대한 걱정을 없앨 수 있다.

 

Wrap Up

Bean의 주입 시, 생성자를 많이 사용하는 이유에 대해 알아보았다.

생성자 주입의 장점인 순환참조 방지, 그리고 객체의 불변성을 보장한다는 것으로 마무리하자.