[Spring] 1. IoC Container
IoC와 IoC Container의 정의
1. IoC(Inversion of Control, 제어의 역전)란?
: 일반적인 프로그래밍에서, 프로그램의 제어 흐름 구조가 뒤바뀌는 것
- 객체의 생성, 관리, 객체 간의 의존성 처리 등을 프레임워크에서 대신 처리해주는 것이 IoC의 대표적인 예이다.
ㄴ> 객체를 관리하는 게 개발자가 아니라 IoC container가 주도권을 갖고 처리하는 것을 제어의 역전이라 한다.
2. IoC Container란?
: IoC를 구현한 구체적인 프레임워크(= 제어의 역전이 구현되어 있는 프레임워크)
- IoC Container를 이용하면 객체 생성, 초기화, 의존성 처리 등을 자동으로 수행할 수 있다.
- 대표적인 IoC Container로는 Spring Framework의 ApplicationContext가 있다.
Spring IoC Container
1. Bean이란?
: Spring IoC Container에서 관리되는 객체
- Spring은 bean을 생성하고, 초기화, 의존성 주입, 제거하는 등의 일을 IoC Container를 통해 자동으로 처리할 수 있다.
2. Bean Factory란?
: Spring IoC Container의 가장 기본적인 형태로, Bean의 생성, 초기화, 연결, 제거 등의 라이프 사이클을 관리
- 혼자서 관리하는 것은 아니고, 이를 위해 Configuration Metadata를 사용한다.
3. Configuration Metadata란?
: Bean Factory가 IoC를 적용하기 위해 사용하는 설정 정보
- IoC Container에 의해 관리되는 Bean 객체를 생성하고 구성할 때 사용된다.
* POJOs란?
- Plain Old Java Object로, 외부적인 기술없이 순수하게 만든 자바 클래스이다.
- POJOs를 Spring Container에 맡기는데,
어떻게 사용하라는지에 대한 설정 정보를 Configuration Metadata(설정 정보)를 담아서 맡긴다.
개발자가 짜는 건 이 POJOs(비즈니스 로직)이다.
4. Application Context란?
: Bean Factory를 확장한 IoC Container로, Bean을 등록하고 관리하는 기능은 Bean Factory와 동일하지만,
Spring이 제공하는 각종 부가 기능을 추가로 제공한다.
- ListableBeanFactory: Bean Factory가 제공하는 모든 기능 포함
- ApplicationEventPublisher: 이벤트 처리(Event Handling) 기능 제공
- MessageSource: 국제화(i18n)를 지원하는 메세지를 해결하는 부가 기능 제공
- ResourceLoader: 리소스 핸들링(Resource Handling) 기능 제공
- GenericXmlApplictaionContext: ApplicationContext를 구현한 클래스로, XML MetaData Configuration을 읽어 컨테이너 역할 수행
- AnnotationConfigApplicationContext: ApplicationContext를 구현한 클래스로, Java MetaData Configuration을 읽어 컨테이너 역할 수행
- cf. XML로 MetaData를 전달하는 거랑 Java로 MetaData를 전달하는 거랑 Application Context가 다르다.
IoC Container 사용하기
IntelliJ에서 새 프로젝트를 만든 뒤 build.gradle에 아래 라이브러리(Spring Context, Project Lombok)를 추가해준다.
("MVN REPOSITORY" 에서 복사할 수 있다.)
// https://mvnrepository.com/artifact/org.springframework/spring-context
implementation 'org.springframework:spring-context:6.1.12'
// https://mvnrepository.com/artifact/org.projectlombok/lombok
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
build.gradle은 아래와 같다.
plugins {
id 'java'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/org.springframework/spring-context
implementation 'org.springframework:spring-context:6.1.12'
// https://mvnrepository.com/artifact/org.projectlombok/lombok
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
테스트에 공통적으로 사용할 MemberDTO 클래스이다.
@Getter
@Setter
@ToString
@AllArgsConstructor // 모든 멤버 변수를 파라미터로 받는 생성자 생성
public class MemberDTO {
private int sequence;
private String id;
private String pwd;
private String name;
}
1. XML-based Configuration
(1) GenericApplicationContext
- GenericXmlApplicationContext는 Spring IoC Container 중 하나로, XML 형태의 Configuration Metadata를 사용하여 Bean를 생성한다.
// java\org\example\section01\xmlconfig\Application
public class Application {
public static void main(String[] args) {
ApplicationContext applicationContext
= new GenericXmlApplicationContext("section01/xmlconfig/spring-context.xml");
MemberDTO member1 = (MemberDTO) applicationContext.getBean("member"); // bean id
/* MemberDTO 타입의 빈이 하나만 정의되어 있는 경우 그 빈이 member2에 들어가겠지만,
해당 타입의 빈이 여러 개 정의되어 있는 경우 NoUniqueBeanDefinitionException이 발생한다.*/
MemberDTO member2 = applicationContext.getBean(MemberDTO.class); // 클래스 메타 정보
MemberDTO member3 = applicationContext.getBean("member", MemberDTO.class);
System.out.println(member3);
}
}
(2) XML 기반 Configuration Metadata 파일
<!--resources/section01/xmlconfig/spring-context.xml-->
<!--아래 4행은 xml 파일을 생성하면 자동으로 입력되어 있다.-->
<?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">
<!-- MemberDTO 타입의 bean 등록 -->
<bean id="member" class="com.ohgiraffers.common.MemberDTO">
<!--<constructor-arg> 태그는 생성자를 호출할 때 전달할 인자를 정의한다.-->
<!--만약 <beans> 태그 내부에 아무것도 작성하지 않으면 기본 생성자를 사용한다는 의미이다.-->
<constructor-arg index="0" value="1"/> <!--첫번째 생성자 인자로 1 전달-->
<constructor-arg name="id" value="user01"/> <!--생성자 인자 중 이름이 id인 인자로 'user01' 전달-->
<constructor-arg index="2"><value>pass01</value></constructor-arg> <!--세번째 생성자 인자로 'pass01' 전달-->
<constructor-arg name="name"><value>홍길동</value></constructor-arg> <!--생성자 인자 중 이름이 name인 인자로 '홍길동' 전달-->
</bean>
</beans>
-
2. Java-based Configuration
(1) AnnotationConfigApplicationContext
- AnnotationConfigApplicationContext는 Spring IoC Container 중 하나로, Java Configuration 형태의 Configuration Metadata를 사용하여 Bean을 생성한다.
// java\org\example\section02\javaconfig\Application
public class Application {
public static void main(String[] args) {
// 생성자에 @Configuration 어노테이션이 달린 설정 클래스(여기선 ConfigurationContext)의 메타 정보 전달
ApplicationContext context =
new AnnotationConfigApplicationContext(ConfigurationContext.class);
MemberDTO member = context.getBean("member", MemberDTO.class);
System.out.println(member);
}
}
(2) Java 기반 Configuration Metadata
- Java Configuration 형태의 Configuration Metadata 파일은 아래와 같이 작성할 수 있다. 아래에서는 MemberDTO 클래스의 객체를 Bean으로 등록하고 있다.
// java\org\example\section02\javaconfig\ConfigurationContext
@Configuration("configurationSection02") // 해당 클래스가 빈을 생성하는 설정 클래스임을 표기. 괄호 안의 것은 이름을 붙여준 것.
public class ConfigurationContext {
@Bean(name = "member") // Bean의 이름은 자동으로 메소드의 이름을 따라가는데, 괄호 안에서 이름을 지정해줄 수 있다.
public MemberDTO getMember() { // 반환 타입 중요
return new MemberDTO(1, "user01", "pass01", "홍길동");
}
}
3. Annotation-based Configuration
* Bean을 등록하는 방법
1) xml 파일에 bean 태그를 통해 등록
2) 메소드 위에 @bean 어노테이션을 붙여 등록
3) 클래스 위에 @Component를 붙이면 IoC container가 얘를 스캔해가는 작업을 통해 등록(component scan)
-> 이번에 다루는 것
(1) @ComponentScan이란?
: base package로 설정된 하위 경로에 특정 어노테이션을 가지고 있는 클래스를 bean으로 등록하는 기능
- @Componenet 어노테이션이 작성된 클래스를 인식하여 bean으로 등록한다.
/*
이 base package 하위의 모든 패키지에서 @Component가 붙어있는 클래스들은 다 스캔해서 빈 등록한다.
base package 설정이 별도로 없을 경우엔 현재 패키지 기준으로 스캔이 수행된다.
*/
@ComponentScan(basePackages = "org.example")
- @Component("name") 또는 @Component(value="name") 형식으로 bean의 id를 설정할 수 있다. 이름을 별도로 지정하지 않으면 클래스명의 첫 문자를 소문자로 변경하여 bean의 id로 자동 인식한다.
- 특수 목적에 따라 세부 기능을 제공하는 @Controller, @Service, @Repository, @Configuration 등을 사용한다.
@Component | 객체를 나타내는 일반적인 타입으로 <bean> 태그와 동일한 역할 |
@Controller | 프리젠테이션 레이어, 웹 어플리케이션에서 View에서 전달된 웹 요청과 응답을 처리하는 클래스 ex) Controller Class |
@Service | 서비스 레이어, 비즈니스 로직을 가진 클래스 ex) Service Class |
@Repository | 퍼시스턴스(persistence) 레이어, 영속성을 가지는 속성(파일, 데이터베이스)을 가진 클래스 ex) Data Access Object Class |
@Configuration | 빈을 등록하는 설정 클래스 |
(2) @ComponentScan 어노테이션으로 base packages 설정하기
@Configuration("configurationSection03")
@ComponentScan(basePackages = "org.example")
public class ConfigurationContext {
}
public class Application {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(ConfigurationContext.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName: " + beanName);
}
}
}
실행결과는 위와 같다.
위에서 4개는 context 쪽에서 자동으로 내부에서 등록되는 bean이고,
@Configuration을 통해 등록된 "configurationSection03"와 "configurationSection02",
@Component을 통해 등록된 MemberDAO,
@Bean을 통해 등록된 member,
총 4개의 bean이 추가로 등록되어 있는 것을 확인할 수 있다.
(3) XML에서 Component Scan 설정하기
<?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">
<!--이런 식으로 XML 설정 파일에서 Component Scan의 base package를 설정할 수도 있다.-->
<context:component-scan base-package="org.example"/>
</beans>
public class Application {
public static void main(String[] args) {
ApplicationContext applicationContext
= new GenericXmlApplicationContext("section03/annotationconfig/spring-context.xml");
String[] beanNames = applicationContext.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName: " + beanName);
}
}
}
정리
- XML 설정은 전통적으로 사용하던 방식으로, 최근에는 Java 설정이 선호된다.
- 개발자가 직접 컨트롤 가능한 클래스의 경우 @Component를 클래스에 사용하여 빈 스캐닝을 통해 자동 빈 등록을 한다.
- 개발자가 직접 제어할 수 없는 외부 라이브러리는 @Bean을 메소드에 사용하여 수동 빈 등록을 한다.
- 다형성을 활용하고 싶은 경우에도 @Bean을 사용할 수 있다.
* 출처
https://docs.spring.io/spring-framework/reference/core/beans/basics.html
Container Overview :: Spring Framework
As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata. This configuration metadata represents how you, as an application developer, tell the Spring container to instantiate, configure, and assemble the component
docs.spring.io