Spring Bean과 Instance

MyeongJae Lee·2022년 1월 23일
0

Record

목록 보기
1/2

@Controller, @Service, @Repository 등 애너테이션을 통해 bean을 생성하고 DI(의존관계 주입)를 해왔다. 그런데 직접 생성한 인스턴스가 위 bean의 역할을 대체할 수 없을까?

이를 실험하기 위해 @Service를 Instance생성해서 대체해봤다.

Spring Bean

늘 사용해오던 방법을 사용했다.

@Controller
public class IocController {
	
    private final IocService iocService;
    
    @Autowired
    public IocController(IocService iocService) {
    	this.iocService = iocService;
    }
    
    ...
}

@Service
public class IocServiceImpl implements IocService {
	
    @Autowired
    private IocMapper iocMapper;
    
    ...
}

Service에서 Mapper(Repository)와 생성자 DI가 아닌 필드 DI를 한 이유는 다음에 나올 Instance 경우와 같은 환경으로 맞추기 위해서다.(스포지만 intelliJ에서 에러를 띄워준다.)

Controller와 Service를 제외하고는 모두 같다.

Instance

Service를 애너테이션이 아닌 new ServiceImpl();을 통해 생성했다.

@Controller
public class IocController {
	
    private final IocService iocService;
    
    @Autowired
    public IocController() {
    	this.iocService = new IocServiceImpl();
    }
    
    ...
}

public class IocServiceImpl implements IocService {
	
    @Autowired
    private IocMapper iocMapper;
    
    ...
}

Controller에서 생성자 DI를 할 때, new IocServiceImpl();을 통해 Instance를 직접 생성해서 진행했다. 그리고 @Service 애너테이션을 없애서 service가 bean으로 생성되는 것을 막았다.

Spring Bean을 생성한 경우, 에러없이 잘 동작한다. 하지만 Instance를 직접 생성한 경우, 에러가 발생한다.

추가

IoC(Inversion of Control) 제어의 역전

우리는 new 연산자를 통해 Instance를 생성해서 클래스를 사용한다. 그런데 평소에 우리는 @Controller, @Service, @Repository, @Component, @Bean만 선언했을 뿐, new 연산자를 사용하지 않는다. 그런데도 잘 동작하는 이유는 무엇일까? 이 부분에서 IoC를 엿볼 수 있다.

IoC, 제어의 역전, 즉 개발자가 직접 인스턴스를 생성하고(new 연산자를 사용) 의존관계를 직접 주입해주는 것이 아니라 IoC container가 대신하는 것이다. 개발자가 가지고 있던 제어권을 IoC container에게 넘겨주는 것이다. 그렇기 때문에 우리는 new 연산자 없이 애너테이션 만으로 Instance를 생성해 사용할 수 있다.

IoC container가 생성한 Instance를 bean(spring bean)이라고 한다.

BeanFactory는 IoC container의 기능을 정의하고 있는 interface이다. Bean의 생성, Bean 사이의 의존관계 주입(DI), Bean의 lifecycle 관리 등에 대한 기능을 정의하고 있다.
ApplicationContext는 BeanFactory를 상속받는 interface로 BeanFactory의 기능 뿐만 아니라, AOP, message 등 추가적인 기능에 대해 정의되어 있다.

특별한 경우가 아니라면 변수선언타입으로 ApplicationContext를 사용하는 것이 좋다고 한다.

DI(Dependency Injection) 의존관계 주입

Controller에서 Service의 비즈니스 로직을 사용하기 위해 controller의 필드에 service를 변수(또는 상수)로 선언한다. 이 때 의존관계를 주입할 수 있다. IoC container는 Bean으로 생성된 Instance들의 타입을 가지고 있다가, 의존관계가 주입될 때, 해당되는 타입을 찾아 주입하게 된다.

의존관계 주입에는 여러 방법이 있다.

Field Injection

@Autowired
private Service service;

위와 같이 @Autowired 애너테이션을 사용해서 의존관계를 주입하는 경우이다. 매우 편리하지만, 추천하는 방법은 아니다. intelliJ에서는 IDE에서 노란 밑줄로 추천하지 않는 것을 보여준다.

Setter Injection

private Service service;

public void setService(Service service) {
	this.service = service;
}

위와 같이 setter를 통해 의존관계를 주입한다. setService()가 public으로 선언되어있기 때문에, 어떠한 이유로 인해 의도치 않게 의존관계가 잘못 주입될 수 있다. 따라서 추천하는 방법은 아니다.

Constructor Injection

private final Service service;

public Controller(Service service) {
	this.service = service;
}

위와 같이 생성자를 통해 의존관계를 주입한다. 가장 추천하는 방법이다. controller는 IoC container에 의해 싱글톤으로 생성되며, 따라서 생성자는 단 한번만 사용된다. 그렇기 때문에 처음 생성 시 주입한 의존관계가 어떠한 이유에도 변할 수 없다.

profile
개발자가 하고싶어요

0개의 댓글