
원래 자바 프로그램은 내가 직접:
제어권이 개발자 코드에 있다.
IoC는 이 흐름을 반대로 뒤집는다고 생각하면 된다.
객체 생성·초기화·연결·소멸, 의존성 관리 같은 걸
“프레임워크(컨테이너)가 대신 제어”하는 것
개발자는 “어떤 객체가 필요하다”만 선언하고, 실제 생성/주입은 스프링이 자동으로 해준다.
IoC를 실제로 구현한 “상자(컨테이너)”가 IoC 컨테이너다.
대표적인 IoC 컨테이너가 스프링의 ApplicationContext이다.
스프링 IoC 컨테이너가 관리하는 객체
- 내가
new하지 않음- 컨테이너가 생성하고, 등록하고, 주입해 주는 객체
- IoC 컨테이너 안의 객체 = Bean
MemberDTO member = context.getBean(MemberDTO.class);
이렇게 꺼내 쓰는 애들을 Bean이라고 보면 된다.
스프링 IoC 컨테이너의 가장 기본 형태
- Bean 생성, 초기화, 의존성 연결, 소멸 등 Bean의 라이프사이클 관리
- “최소 기능”만 있는 IoC 컨테이너
BeanFactory + 각종 편의 기능을 더한 컨테이너
BeanFactory 기능은 그대로 갖고 있으면서 아래의 기능들도 수행할 수 있다.
ApplicationEventPublisher : 이벤트 발행/구독MessageSource : 국제화(i18n) 메시지ResourceLoader : 리소스 파일 로딩구현체 예시:
GenericXmlApplicationContext → XML 설정 파일을 읽어서 컨테이너 생성AnnotationConfigApplicationContext → @Configuration 자바 클래스를 읽어서 컨테이너 생성스프링을 쓸 때 가장 기본적으로 사용하는 컨테이너인 것.
Bean의 등록 방법에 따른 방법이 아래와 같이 있다.
@Configuration + @Bean)@Component, @Service …)ApplicationContext context =
new GenericXmlApplicationContext("section01/xmlconfig/spring-context.xml");
여기서 "spring-context.xml" 이 Configuration Metadata(설정 정보) 파일.
<beans ...>
<bean id="member" class="com.ohgiraffers.common.MemberDTO">
<constructor-arg index="0" value="1"/>
<constructor-arg name="id" value="user01"/>
<constructor-arg index="2"><value>pass01</value></constructor-arg>
<constructor-arg name="name"><value>홍길동</value></constructor-arg>
</bean>
</beans>
이를 기존 코드로 해석하면 아래와 같다.
MemberDTO memberDto = new MemberDto(1, "user01", "pass01", "홍길동");
이렇게 객체를 만들어주는거다.
핵심만 보면:
<bean> : Bean 객체 하나를 등록id : Bean 이름 (나중에 getBean 할 때 사용) → MemberDto 의 객체 new 대신 사용됨class : 실제 자바 클래스<constructor-arg> : 생성자에 전달할 값(매개변수 생성자의 매개변수 역할을 함)// 1) id로 꺼내기
MemberDTO m1 = (MemberDTO) context.getBean("member");
// 2) 타입으로 꺼내기
MemberDTO m2 = context.getBean(MemberDTO.class);
// 3) id + 타입으로 꺼내기 - 형변환 하지 않아도 된다.
MemberDTO m3 = context.getBean("member", MemberDTO.class);
XML 대신 자바 클래스로 설정을 작성하는 방식.
ApplicationContext context =
new AnnotationConfigApplicationContext(ContextConfiguration.class);
여기서 ContextConfiguration.class 가 설정 클래스.
ApplicationContext객체를 생성해서 IOC 컨테이너에 메타데이터를 만들겠다는 것.
@Configuration
public class ContextConfiguration {
@Bean(name = "member")
public MemberDTO getMember() {
return new MemberDTO(1, "user01", "pass01", "홍길동");
}
}
포인트:
@Configuration → 이 클래스는 Bean을 등록하는 설정 클래스로 선언하는 어노테이션@Bean → 이 메서드의 반환 객체를 컨테이너에 Bean으로 등록하라는 의미를 가진다. IOC 에서 이 객체가 생성된다는 사실을 알아야 해서 Bean 어노테이션을 붙인다. → 이름을 별도로 붙이지 않으면 bean의 id로 자동인식된다. @Bean("myName") 또는 @Bean(name="myName") 의 형식으로 bean의 이름의 id를 설정할 수 있다.name="member" → Bean 이름 설정 (생략하면 메서드명 getMember가 id가 됨)MemberDTO member = context.getBean("member", MemberDTO.class);
XML 방식과 꺼내는 법은 같다.
다른 건 등록의 차이 밖에 없다. (XML vs 자바 코드)
/*이제 멤버를 들고오는 방법은 IOC 컨테이너 applicationContext 의 .getBean 으로 Bean 객체 이름을 매핑하여 들고온다.*/
MemberDao memberDao = applicationContext.getBean("memberDao", MemberDao.class);
System.out.println("memberDao = " + memberDao.selectMember(1));
//결과
memberDao = MemberDto(sequence=1, id=user01, pwd=pass01, name=홍길동)
이 방식은 개발자가 직접 @Bean으로 하나하나 등록하는 대신, 스프링이 패키지를 스캔하면서 자동으로 Bean 등록하게 만드는 것.
@Component
public class MemberDAO {
private final Map<Integer, MemberDTO> memberMap = new HashMap<>();
public MemberDAO() {
memberMap.put(1, new MemberDTO(1, "user01", "pass01", "홍길동"));
memberMap.put(2, new MemberDTO(2, "user02", "pass02", "유관순"));
}
public MemberDTO selectMember(int sequence) {
return memberMap.get(sequence);
}
}
@Component → 이 클래스를 자동으로 Bean으로 등록하라는 뜻memberDAO)특수 역할별로 쪼개 놓은 어노테이션도 있다. 계층이 정해져 있는 경우 아래 어노테이션을 사용할 수 있다.
@Controller : 웹 요청/응답 처리@Service : 비즈니스 로직@Repository : DB 접근@Configuration : 설정 클래스실제로는 내부적으로 전부 @Component 의 특수 버전이다. 만약 여기에 해당되지 않는 경우의 것들은 Bean에 전부 알아서 등록해주어야 한다.
Componenet의 기본 범위는 설정 파일이 있는 패키지를 기준으로 사용한다. 따라서 Bean을 어느 범위까지 사용할 것인가에 따라서 Bean의 범위를 등록해야 한다.
@ComponentScan(basePackages = "com.ohgiraffers")
public class ContextConfiguration {}
@ComponentScan : basePackage로 설정된 하위 경로에 특정 어노테이션을 가지고 있는 클래스를 bean으로 등록하는 기능이다. @Component 어노테이션이 작성된 클래스를 인식해서 bean으로 등록한다.basePackages : Configuration 범위를 얘로 정해줌. 여기 내부를 돌면서 아래를 수행.@Component, @Service, @Repository, @Controller 등이 붙은 클래스를 Bean으로 등록.테스트:
ApplicationContext context =
new AnnotationConfigApplicationContext(ContextConfiguration.class);
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println("beanName : " + beanName);
}
memberDAO 가 찍히면 Bean 등록 성공.
getBeanDefinitionNames() : 스프링 컨테이너에서 생성된 bean들의 이름을 배열로 반환기본적으로 @Component류는 다 스캔 대상이지만,
원하지 않는 클래스는 제외하거나,
특정 클래스만 포함시키고 싶을 수도 있다.
@ComponentScan(
basePackages = "com.ohgiraffers",
excludeFilters = {
@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = { MemberDAO.class }
)
}
)
public class ContextConfiguration {}
→ 이렇게 하면 MemberDAO 는 Bean으로 등록되지 않음.
@ComponentScan(
basePackages = "com.ohgiraffers",
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = { MemberDAO.class }
)
}
)
public class ContextConfiguration {}
useDefaultFilters = false 로 하면 기본 @Component 스캔을 꺼버리고XML 방식에서도 Component Scan 가능:
<beans ... 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.ohgiraffers"/>
</beans>
필요하면 <context:exclude-filter> 로 제외도 가능하다.