📁 Bean
스프링에서의 객체
기존 자바 프로그래밍에서 객체를 생성하기 위해서는 클래스를 만들고 new 연산자를 이용해 직접 인스턴스를 생성했다.
그러나 스프링은 프로그램의 제어권을 가지기 위해 자바 객체를 자신만의 형태로 바꾸고, Bean이라고 칭한다.
객체를 Bean으로 등록한다는 것은 곧 객체의 제어권을 스프링 컨테이너로 넘긴다는 것을 의미한다.
📁 스프링 컨테이너
빈을 관리하는 컨테이너
싱글톤 레지스트리, 의존성 주입, AOP 등 여러 기능들을 제공하고 빈의 생명주기를 관리한다.
🤔 싱글톤 Singleton
스프링은 기본적으로 싱글톤으로 빈을 생성하여 매 요청마다 같은 객체를 돌려준다.
매번 새로운 객체를 생성하는 것보다 이미 생성된 객체를 가져오는 것이 효율적이기 때문이다.
서버의 규모가 커지면 처리해야 할 요청이 많아지는데, 요청이 들어올 때마다 새로운 객체를 만들어서 사용하면 서버의 부하가 심해지게 된다.
따라서 싱글톤으로 bean을 만들고 애플리케이션에서 전역적으로 접근 가능하도록 해 필요한 영역에서 공유하도록 하는 것이다.
🤔 의존성 주입 DI
스프링은 외부에서 의존성을 주입하는 방식을 통해 객체 간의 의존관계를 설정한다.
객체 내부에서 직접 다른 객체를 생성해 의존관계를 만드는 경우 객체 간 결합도가 높아져 유지보수에 어려움이 생긴다.
따라서 스프링에서는 스프링 컨테이너를 이용해 외부에서 객체를 생성하고, 만들어진 객체를 주입하는 의존성 주입 방법을 채택한다.
📁 Bean 등록 방법
스프링에서 빈을 등록하는 것은 자바에서 객체를 생성하는 것과 구조적으로 동일하다.
약간의 방식에 차이가 있는데, 두가지 방법에 대해 알아보자.
클래스 방식
클래스에 어노테이션을 사용해 bean을 추가하고 의존성을 주입하는 방법으로 매우 간단하다.
단, 컴포넌트 스캔의 대상이 되는 클래스들에 한해서만 사용할 수 있는 방식이다.
설정 파일을 통해 컴포넌트 스캔을 시작할 패키지를 명시한다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.spring.project" />
</beans>
컴포넌트 스캔의 대상이 되는 클래스에 @Component를 붙인다.
스프링은 컴포넌트 스캔을 통해 해당 어노테이션을 가진 클래스의 객체를 스프링 컨테이너에 빈으로 등록해준다.
내부에 컴포넌트 어노테이션을 가지고 있는 @Controller, @Service, @Repository 역시 컴포넌트 스캔의 대상이 된다.
@Controller
public class UserController {
XML 방식
xml 설정 파일을 통해 bean을 등록하는 방법이다.
자바에서 클래스를 만들고 객체를 생성하는 방식과 구조적으로는 유사하니, 비교하면서 알아보자.
<bean> 태그에 객체를 만들 때 사용할 이름과 클래스를 지정해 빈을 등록한다.
객체를 만들 때 클래스명과 참조변수를 명시하듯이 class와 id 속성을 기입한다.
User user = new User();
<bean id="user" class="User">
📁 의존성 주입 방법
빈 객체에 의존성을 주입하는 방법은 세가지가 있다.
- 필드 주입
- Setter 주입
- 생성자 주입
어떤 방법을 선택하느냐에 따라 빈의 생명주기가 조금 다르다.
필드 주입, Setter 주입에서는 객체의 생성과 의존성 주입이 각각의 라이프 사이클로 나뉘어 있다.
반면 생성자 주입을 하는 경우 객체 생성과 의존성 주입이 동시에 진행된다.
필드 주입
클래스 방식에서는 단순히 주입받을 객체에 어노테이션을 붙이면 된다.
@Controller
public class UserController {
@Autowired
private UserService uService;
}
Setter 주입
클래스 방식에서는 setter 메서드에 어노테이션을 붙여 의존성을 주입한다.
@Controller
public class UserController {
private UserService uService;
@Autowired
public void setUService(UserService uService) {
this.uService = uService;
}
}
xml 방식에서는 property 태그를 사용한다.
기본 자료형이면 value, 참조 자료형이면 ref 속성을 쓴다.
Job job = new Job();
user.setId(1);
user.setName("마지");
user.setJob(job);
<bean id="job" class="Job"/>
<bean id="user" class="User">
<property name="id" value="1"/>
<property name="name" value="마지"/>
<property name="job" ref="job"/>
</bean>
생성자 주입
클래스 방식에서는 생성자에 어노테이션을 붙여 의존성을 주입한다.
@Controller
public class UserController {
private UserService uService;
@Autowired
public UserController(UserService uService) {
this.uService = uService;
}
}
xml 방식에서는 <constructor-arg> 태그를 사용한다.
Job job = new Job();
User user = new User(job);
<bean id="job" class="Job"/>
<bean id="user" class="User">
<constructor-arg ref="job"/>
</bean>
📌
어노테이션을 통한 의존성 주입은 스프링이 관리하는 객체에 대해서만 가능하다.
반면 XML 방식에서는 개발자가 직접 생성한 객체에 대한 의존성 주입도 가능하다.
따라서 의존관계에 있는 객체의 특성에 따라 어떤 방식을 사용할지 자유롭게 선택할 수 있다.
- 컨트롤러, 서비스, DAO 같이 정형화된 코드의 경우 주로 클래스 방식의 컴포넌트 스캔을 사용한다.
- 정형화되지 않거나 상황에 따라 구현 클래스를 변경해야 하는 경우 설정파일 방식을 사용한다.