Home 스프링 빈 / 스프링 컨테이너
Post
Cancel

스프링 빈 / 스프링 컨테이너

앞선 게시글에서 스프링 프레임워크를 사용하면 객체를 직접 생성하고 관리하는 대신 스프링 빈으로만 등록하면 된다고 했었다. 그럼 스프링 빈이 무엇인지 알아보자.

자바 빈?

자바 빈은 자바 객체를 나타내기 위한 규약(Getter/Setter, 기본생성자 등)일 뿐이다. 스프링 빈과는 전혀 다른 개념이다. 스프링 빈이란 스프링 컨테이너가 관리하는 자바 객체이다. 즉, 개발자가 직접 객체의 생명주기를 관리하지 않고 스프링 컨테이너가 관리하는 객체로 스프링 빈으로 등록해주기만 한다면 의존주입같은 방법으로 스프링 컨테이너로부터 객체를 받아와 사용할 수 있다.

스프링 컨테이너

스프링 컨테이너는 스프링 애플리케이션에서 객체의 생성과 관리를 책임지는 즉, 자바 객체의 생명 주기를 관리하는 역할을 한다. 스프링 컨테이너는 크게 BeanFactoryApplicationContext 가 있는데 BeanFactory 가 기본적인 컨테이너이고 ApplicationContextBeanFactory 를 확장한 컨테이너로 더 많은 기능을 제공한다. 스프링 컨테이너는 XML, 어노테이션 기반 설정 클래스, 컴포넌트 스캔의 방법으로 만들 수 있다. 스프링부트가 나오면서 XML로 생성하는 방법은 거의 사용되지 않는다. 하지만 사용할 일이 생길 수도 있으므로 XML등록부터 하나씩 알아보자.

Bean으로 등록할 객체

Car 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package pjh5365.springbean;

public class Car {
    private Engine engine;

    public Car() {
        System.out.println("Car 기본 생성자");
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.exec();
        System.out.println("엔진을 사용해 운전한다.");
    }
}

Engine 클래스

1
2
3
4
5
6
7
8
9
10
11
12
package pjh5365.springbean;

public class Engine {

    public Engine() {
        System.out.println("Engine 기본 생성자");
    }

    public void exec() {
        System.out.println("엔진이 동작한다.");
    }
}

XML 등록

XML 파일 작성

XML로 빈들을 등록하려면 src/main/resources 폴더에 XML파일(applicationContext.xml)을 생성하고 아래와 같이 파일을 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="engine" class="pjh5365.springbean.Engine"/>
    <bean id="car" class="pjh5365.springbean.Car">
        <!-- Setter를 이용한 의존성 주입 -->
        <property name="engine" ref="engine"/>
        <!-- 생성자를 이용한 의존성 주입 -->
        <!-- <constructor-arg name="engine" ref="engine"/> -->
    </bean>

</beans>

<bean> 태그를 이용해 빈으로 등록할 객체의 클래스명을 패키지와 함께 작성하고 사용할 이름을 id로 지정한다. 위의 코드에선 Car를 car로 Engine을 engine으로 등록했고 Car클래스의 Setter를 사용해 의존성을 주입하기 때문에 <property> 태그로 의존성을 주입했다. 생성자를 사용하여 의존성을 주입할땐 생성자 주입을 만들어 놓고 아래의 <constructor-arg> 태그를 사용한다.

스프링 컨테이너 생성

이제 XML파일로 빈들을 모두 등록해주었으니 빈들을 포함한 스프링 컨테이너를 생성해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package pjh5365.springbean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextXML {

    public static void main(String[] args) {
        // XML파일을 읽어 스프링 컨테이너 생성
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 스프링 컨테이너에서 빈을 꺼내서 사용
        Car car = (Car)ac.getBean("car"); // 형변환 필수
        car.drive();
    }
}

위의 코드는 ClassPathXmlApplicationContext 로 XML파일을 읽어 빈들을 등록하여 사용한다.

실행결과

xml기반 실행결과

어노테이션 기반 설정 클래스 사용

어노테이션 기반 설정 클래스를 사용하면 빈으로 등록할 클래스들을 하나의 설정파일에서 한번에 등록할 수 있다.

설정 클래스

설정 클래스로 지정하려면 @Configuration 어노테이션을 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package pjh5365.springbean;

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

@Configuration // 설정 파일로 지정
public class ApplicationConfig {

    @Bean // 빈으로 등록
    public Car car(Engine engine) {
        Car car = new Car();
        car.setEngine(engine); // Setter 주입
        return car;
    }

    @Bean // 빈으로 등록
    public Engine engine() {
        return new Engine();
    }
}

스프링 컨테이너 생성

AnnotationConfigApplicationContext 를 사용해 어노테이션 기반 설정 클래스를 읽어 스프링 컨테이너를 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package pjh5365.springbean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ApplicationContextConfig {

    public static void main(String[] args) {
        // 어노테이션으로 설정한 설정 클래스를 읽어 스프링 컨테이너 생성
        ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        // 스프링 컨테이너에서 빈을 꺼내서 사용
        Car car = (Car)ac.getBean("car"); // 형변환 필수
        car.drive();
    }
}

실행결과

어노테이션 기반 실행결과

컴포넌트 스캔

컴포넌트 스캔은 스프링이 직접 클래스들을 탐색하면서 빈으로 등록하는 기능이다. 설정 클래스에서 빈으로 등록하지 않아도 클래스를 읽어 빈으로 등록할 수 있으므로 컴포넌트 스캔 기능을 사용하면 설정 코드가 크게 줄어든다. 스프링이 컴포넌트 스캔을 하기 위해선 각 클래스에 @Component 어노테이션이 붙어있어야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package pjh5365.springbean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component // 빈으로 등록
public class Car {

    @Autowired // 의존성 주입
    private Engine engine;

    public Car() {
        System.out.println("Car 기본 생성자");
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.exec();
        System.out.println("엔진을 사용해 운전한다.");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package pjh5365.springbean;

import org.springframework.stereotype.Component;

@Component // 빈으로 등록
public class Engine {

    public Engine() {
        System.out.println("Engine 기본 생성자");
    }

    public void exec() {
        System.out.println("엔진이 동작한다.");
    }
}

@ComponentScan 어노테이션

컴포넌트 스캔을 하기 위해선 설정 클래스에 @ComponentScan 어노테이션을 붙여야 한다. basePackages 으로 스캔할 패키지를 지정해야 정상적인 컴포넌트 스캔이 가능하다.

1
2
3
4
5
6
7
8
9
package pjh5365.springbean;

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

@Configuration
@ComponentScan(basePackages = "pjh5365.springbean") // 스캔할 패키지 지정
public class ApplicationConfigComponentScan {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package pjh5365.springbean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ApplicationContextComponentScan {

    public static void main(String[] args) {
        // 컴포넌트 스캔으로 빈등록하기
        ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfigComponentScan.class);

        // 스프링 컨테이너에서 빈을 꺼내서 사용
        Car car = (Car)ac.getBean("car"); // 형변환 필수
        car.drive();
    }
}

실행결과

컴포넌트 스캔 실행결과

This post is licensed under CC BY 4.0 by the author.

객체 지향 특징 / SOLID 원칙

Docker?