IT 강의 정리/윤재성의 스프링 입문

[섹션2][IoC] 07~10강 IoC컨테이너/ 빈 객체 생성하기/ 빈 객체의 생명주기/ BeanPostProcessor

Nellie Kim 2022. 10. 7. 20:05
728x90

07. IoC 컨테이너

IoC(Inversion of Control, 제어 역전) 

일반적으로 프로그래밍을 작성할 때 프로그램이 흘러가는 흐름이나 생성되는 객체에 대한 제어권을 개발자가 가지는 것과 달리 프레임워크가 가지는 것을 의미한다.

개발자가 직접 객체 생성에 관련된 코드를 작성하는 것이 아닌, 프레임워크가 사용하는 파일에 작성하면 이를 토대로 프레임워크가 객체를 생성하여 반환하고 코드가 동작하는 순서를 결정하게 된다.

 

POJO Class(Plain Old Java Object) 

자바 모델이나, 기능, 프레임워크 등에 따르지 않고 홀로 독립적이며 단순한 기능만을 가진 객체들을 의미한다.

자바에서는 이러한 객체들을 Bean이라고 부른다.

POPO(PHP), POCO(닷넷 프레임워크), PODS(C++), POD(Perl) 등

 

IoC컨테이너의 흐름

- Metadata: xml이나 java코드를 가지고 설정한 정보 (이번 강의에서는 xml만 다룬다)

- java POJO classes: 미리 만들어 놓은 클래스

Spring 컨테이너(IoC컨테이너)가 내가 설정한 Metadata를 읽어서 그 정보를 토대로 만들어 놓은 클래스를 가져와서 객체를 만들어서 반환한다.

IoC 컨테이너의 종류?

1) BeanFactory (옛날버전. 더이상 사용x)

  -클래스를 통해 객체를 생성하고 이를 전달한다.

  -상속 등 객체 간의 관계를 형성하고 관리한다.

  -Bean에 관련된 설정을 위한 xml파일은 즉시 로딩하지만, 객체는 개발자가 요구할 때 생성한다.

  -XmlBeanFactory

 

2) AppicationContext

  -클래스를 통해 객체를 생성하고 이를 전달한다.

  -상속 등 객체 간의 관계를 형성하고 관리한다.

  -국제화 지원 등 문자열에 관련된 다양한 기능을 제공한다.

  -리스너로 등록되어 있는 Bean이 이벤트를 발생시킬 수 있다.

  -Bean에 관련된 설정을 위한 xml 파일은 즉시 로딩하면서 객체를 미리 생성해서 가지고 있다.

  -ClassPathXmlApplicationContext

  -FileSystemXmlApplicationContext

  -XmlWebApplicationContext

 

 

IoC 컨테이너 테스트

bean객체를 정의하는 코드를 가진 xml파일(beans.xml)이 패키지 내부에 있느냐, 외부에 있느냐에 따라서 테스트하는 코드가 달라진다. 비교하며 보자.

package kr.co.softcampus.main;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;

import kr.co.softcampus.beans.TestBean;

public class MainClass {

	public static void main(String[] args) {
		// test1();
		//test2();
		//test3();
		test4();
	}

	//BeanFactory(잘 사용x) - 패키지 내부 
	public static void test1() {
		ClassPathResource res = new ClassPathResource("kr/co/softcampus/config/beans.xml");
		XmlBeanFactory factory = new XmlBeanFactory(res); //이때 객체 생성x
		
		TestBean t1 = factory.getBean("t1", TestBean.class); //이때 객체 생성o
		System.out.println(t1);
		
		TestBean t2 = factory.getBean("t1", TestBean.class); //같은 객체를 불러온다.
		System.out.println(t2);
	}
	
	//BeanFactory - 패키지 외부
	public static void test2() {
		FileSystemResource res = new FileSystemResource("beans.xml");
		XmlBeanFactory factory = new XmlBeanFactory(res); //이때 객체 생성x
		
		TestBean t1 = factory.getBean("t2", TestBean.class); //이때 객체 생성o
		System.out.println(t1);
		
		TestBean t2 = factory.getBean("t2", TestBean.class);  //같은 객체를 불러온다.
		System.out.println(t2);
		
	}
	
	// ApplicationContext - 패키지 내부
	public static void test3() {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
			//이때 객체 생성. 디폴드가 beans.xml에 정의되어있는 객체들이 자동으로 생성. but 설정을 통해 getBean호출시 객체 생성하도록 할 수 있다.
		
		TestBean t1 = ctx.getBean("t1", TestBean.class);//이미 만들어진 객체의 주소값을 불러온다.
		System.out.println(t1);
		
		TestBean t2 = ctx.getBean("t1", TestBean.class);//같은 객체를 불러온다.
		System.out.println(t2);
		
		ctx.close();
	}
	
	// ApplicationContext - 패키지 외부
	public static void test4() {
		FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
		
		TestBean t1 = ctx.getBean("t2", TestBean.class);//이미 만들어진 객체의 주소값을 불러온다.
		System.out.println(t1);
		
		TestBean t2 = ctx.getBean("t2", TestBean.class);//같은 객체를 불러온다.
		System.out.println(t2);
		
		ctx.close();
		
	}	
}

이렇게 스프링 프레임워크는 IoC컨테이너를 이용해 Bean객체들을 관리한다.

 

08. bean 객체 생성하기

bean 태그의 기본 속성

 

beans.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">
	
	<!-- xml을 로딩할 때 자동으로 객체가 생성된다. -->					
	<!-- 현재까지의 진도상 id속성이 없다면 객체의 주소값을 받아서 사용할 수 없다. 후에 자동주입 배울것. -->
	<bean class='kr.co.softcampus.beans.TestBean'/>
	
	<!-- xml을 로딩할 때 자동으로 객체가 생성된다. -->
	<!-- id 속성에 이름을 부여하면 getBean메서드를 통해 객체의 주소 값을 받아 사용할 수 있다.-->
	<!-- 생성된 객체는 더이상 생성되지 않는다. Singleton. 호출시 마다 계속 같은 객체 주소값을 반환한다는 뜻 -->
	<bean id='test1' class='kr.co.softcampus.beans.TestBean'/>
	
	<!-- lazy-init : true - xml을 로딩할 때 객체가 생성되지 않는다.(생략하면 false) -->
	<!-- getBean 메서드를 호출할 때 객체가 생성되며 singleton이기 때문에 객체는 하나만 생성된다. -->
	<bean id='test2' class='kr.co.softcampus.beans.TestBean' lazy-init="true"/>
	
	<!-- scope : prototype - xml을 로딩할 때 객체가 생성되지 않는다. -->
	<!-- getBean 메서드를 호출할 때마다 새로운 객체를 생성해서 반환한다. singleton아님-->
	<bean id='test3' class='kr.co.softcampus.beans.TestBean' scope ="prototype"/>
	
</beans>

 

MainClass작성

package kr.co.softcampus.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.co.softcampus.beans.TestBean;

public class MainClass {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
	
		//id 가 test1인 bean객체의 주소값을 받아온다.
		TestBean t1 = ctx.getBean("test1", TestBean.class);
		System.out.printf("t1 : %s\n", t1);
		
		TestBean t2 = ctx.getBean("test1", TestBean.class);
		System.out.printf("t2 : %s\n", t2);
		
		//id 가 test2인 bean객체의 주소값을 받아온다.
		TestBean t3 = ctx.getBean("test2", TestBean.class);
		System.out.printf("t3 : %s\n", t3);
		
		TestBean t4 = ctx.getBean("test2", TestBean.class);
		System.out.printf("t4 : %s\n", t4);
		
		//id 가 test3인 bean객체의 주소값을 받아온다.
		TestBean t5 = ctx.getBean("test3", TestBean.class);
		System.out.printf("t5 : %s\n", t5);
		
		TestBean t6 = ctx.getBean("test3", TestBean.class);//다른 주소값 생성
		System.out.printf("t6 : %s\n", t6);
		
		ctx.close();
	}
}

4개 파일

 

 

09. bean 객체의 생명 주기

- spring의 bean은 Singleton인 경우 xml파일을 로딩할 때 객체가 생성된다.

- Singleton이고 lazy-init 속성이 true일 경우 getBean메서드를 사용할 때 객체가 생성된다.(xml파일을 로딩할때x)

- prototype일 경우 getBean메서드를 사용할 때 매번 새로운 객체가 생성된다.

- IoC컨테이너가 종료 될 때 객체가 소멸 된다. (ctx.close();)

 

MainClass.java

package kr.co.softcampus.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.co.softcampus.beans.TestBean1;
import kr.co.softcampus.beans.TestBean2;

public class MainClass {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
		
		TestBean1 t1 = ctx.getBean("t1", TestBean1.class);
		System.out.printf("t1: %s\n", t1);
		
		System.out.println("------------------------------------------");
		
		TestBean2 t2 = ctx.getBean("t2", TestBean2.class);
		System.out.printf("t2: %s\n", t2);
		
		System.out.println("------------------------------------------");
		
		ctx.close();
	}
}

TestBean1.java

package kr.co.softcampus.beans;

public class TestBean1 {
	public TestBean1() {
		System.out.println("TestBean1의 생성자입니다");
	}
	
	public void bean1_init() {
		System.out.println("TestBean1의 init 메서드");
	}
	
	public void bean1_destroy() {
		System.out.println("TestBean1의 destroy 메서드");
	}
}

TestBean2.java

package kr.co.softcampus.beans;
public class TestBean2 {
	
	public TestBean2() {
		System.out.println("TestBean2의 생성자");
	}
	
	public void default_init() {
		System.out.println("TestBean2의 default_init");
	}
	
	public void default_destroy() {
		System.out.println("TestBean2의 default_destroy");
	}
}

beans.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"
	default-init-method='default_init' default-destroy-method='default_destroy'>
						
	<bean id='t1' class='kr.co.softcampus.beans.TestBean1' lazy-init='true' init-method='bean1_init' destroy-method='bean1_destroy' /> 		
	
	<bean id='t2' class='kr.co.softcampus.beans.TestBean2' lazy-init='true' />			
</beans>

default-init-method, default-destroy-method는 beans태그 안에 써준다.

init-method, destroy-method가 없으면 default-init-method, default-destroy-method를 호출한다.

둘다 있으면 당연히 init-method, destroy-method를 호출한다.

default-init-method, default-destroy-method 둘다 없으면 무시한다.

init-method, destroy-method 둘다 없으면 오류가 발생한다.

 

10. BeanPostProcessor

TestBeanPostProcessor.java

package kr.co.softcampus.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class TestBeanPostProcessor implements BeanPostProcessor{
	
	// init-method 호출 전
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("before");
		return bean; //bean객체 주소값 반환
	}

	// init-method 호출 전
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("after");
		return bean;
	}
}

 

beans.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">
						
	<bean id='t1' class='kr.co.softcampus.beans.TestBean1' lazy-init='true' init-method='bean1_init' /> 		
	
	<bean id='t2' class='kr.co.softcampus.beans.TestBean2' lazy-init='true' init-method='bean2_init'/>	
	
	<!-- 생성된 bean은 스프링프레임워크에서 직접생성을 한다. bean으로 정의해주기만 하면 된다. getBean으로 받아쓰는것도 아니어서 id도 필요없다. -->
	<bean class='kr.co.softcampus.processor.TestBeanPostProcessor'/>		
</beans>

postProcessBeforeInitialization → init-method → postProcessAfterInitialization 순으로 진행 된다.

 

init-method가 수행되기전의 init호출을 가로채서 다른 메서드를 호출한다. bean객체를 반환하여 작업 할 것을 하기도 한다.

 

 

출처 :  [인프런]윤재성의 스프링 프레임워크 개발자를 위한 실습을 통한 입문 과정