빈 라이프사이클과 범위
01 컨테이너의 초기화와 종료
1 2 3 4 5 | GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml"); Greeter g = ctx.getBean("greeter", Greeter.class); String msg = g.greet("스프링"); System.out.println(msg); ctx.close(); | cs |
//컨테이너 초기화
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
//컨테이너의 사용
//컨테이너의 종료
1. GenericApplicationContext 의 생성자를 이용해서 객체를 생성한다 . 이 시점에 스프링컨테이너를 초기화한다. 스프링 컨테이너는 설정 정보를 읽어와 맞은 빈 객체를 생성하고 각 빈을 연결(의존 주입하는) 작업을 수행
2.컨테이너가 초기화가 완료되면 컨테이너를 활용할 수있다. 컨테이너를 사용한다는 것은 getBean()과 같은 메서드를 이용해서 컨테이너에 보관된 빈 객체를 사용한다는 것을 의미한다.
3. 컨테이너의 사용이 이끝나면 컨테이너를 종료한다. 컨테이너를 종료할때 메서드는 close이다 .
컨테이너를 초기화하고 종료할때에는 다음의 작업도 함께 수행한다.
- 컨테이너 초기화 -> 빈 객체의 생성과 의존 객체 주입 및 초기화
- 컨테이너 종료 -> 빈 객체의 소멸
스프링 컨테이너의 라이프 사이클에 따라 빈객체도 자연스럽게 생성과 소멸이라는 라이프사이클을 갖는다.
02 빈객체의 라이프 사이클
스프링 컨테이너를 초기화할때
1. 스프링컨테이너는 가장 먼저 빈 객체를 생성한다.
2. <property>로 지정된 의존을 설정한다. 의존 자동 주입을 통한 의존 설정도 이 시점에 수행
3. 모든 의존 설정이 완료되면 , 빈 객체의 초기화를 수행한다. 빈객체를 초기화 하기위해 스프링은 빈객체의 지정한 메서드를 호출한다.
4. 스프링 컨테이너를 종료하면 스프링 컨테이너는 빈 객체의 지정한 메서드를 호출해서 빈객체의 소멸을 처리한다.
2.1 빈 객체의 초기화와 소멸 : 스프링 인터페이스
빈객체가 InitalizingBean 인터페이스를 구현한 경우,
스프링 컨테이너는 초기화 과정에서 빈 객체의 afterPropertiesSet() 매서드를 실행한다.
빈객체가 생성된뒤에 초기화 과정이 필요하면 InitalizingBean 인터페이스를 구현하고 afterPropertiesSet()메서드에 알맞게 구현해주면 된다.
비슷하게 , 스프링 컨테이너는 빈 객체가 DisposableBean 인터페이스를 구현한 경우, 소멸 과정에서 빈 객체의 destory() 메서드를 실행한다.
따라서, 빈 객체의 소멸과정이 필요한경우 DisposableBean 인터페이스를 상속하고 destory() 메서드를 알맞게 구현하면된다.
초기화 소멸 과정이 필요한 예는 커넥션 풀
커넥션 풀을 위한
빈 객체른 초기화 과정에서 데이터 베이스의와의 연결을 생성한다. 컨테이너를 사용하는 동안 연결을 하고 , 빈 객체를 소멸할때 사용중인
데이터를 연결을 생성한다. 컨테이너를 사용하는 동안 연결을 사용하고 빈객체를 소멸할때 사용중인 데이터 베이스 연결을 끊어 주어야한다.
또 다른 예로는 채팅 클라이언트를 만든다고 가정하다.
어플리케이션이 시작될때 서버와 연결을 생성하고 어플리케이션이 종료될때 연결을 끊을것이다.
이때 서버와 연결을 생성하고 끊는 작업을 초기화 시점과 소멸 시점에 수행 하면 된다.
InitializingBean 인터페이스와 DisposableBean 인터페이스를 구현한 간단한 클래스를 통해서 실제로 초기화 메서드와 소멸 메서드가 언제 실행되는지 확인해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package spring; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; public class Client implements InitializingBean,DisposableBean{ private String host; public void setHost(String host) { this.host = host; System.out.println("Client.setHost() 실행"); } public void send() { System.out.println("Client.send()to"+host); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Client.afterPropertiesSet() 실행"); } @Override public void destroy() throws Exception { System.out.println("Client.destroy() 실행"); } } | cs |
메서드가 호출될때마다 관련 메시지를 출력하도록 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?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="client" class="spring.Client"> <property name="host" value="서버" /> </bean> </beans> | cs |
관련 빈을 등록한 XML 파일
스프링컨테이너를 생성하고, 컨테이너로부터 Client 빈객체를 구해 사용하는 코드 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package main; import org.springframework.context.support.GenericXmlApplicationContext; import spring.Client; public class Main { public static void main(String[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:springconf.xml"); Client client=ctx.getBean("client",Client.class); client.send(); ctx.close(); } } | cs |
실행 결과
1 2 3 4 5 6 7 8 9 10 11 | 3월 19, 2019 9:07:40 오전 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 정보: Loading XML bean definitions from class path resource [springconf.xml] 3월 19, 2019 9:07:40 오전 org.springframework.context.support.GenericXmlApplicationContext prepareRefresh 정보: Refreshing org.springframework.context.support.GenericXmlApplicationContext@66a29884: startup date [Tue Mar 19 09:07:40 KST 2019]; root of context hierarchy Client.setHost() 실행 Client.afterPropertiesSet() 실행 Client.send()to서버 3월 19, 2019 9:07:40 오전 org.springframework.context.support.GenericXmlApplicationContext doClose 정보: Closing org.springframework.context.support.GenericXmlApplicationContext@66a29884: startup date [Tue Mar 19 09:07:40 KST 2019]; root of context hierarchy Client.destroy() 실행 | cs |
setHost 메서드가 호출
afterPropertiesSet 메서드가 호출
Clientsend() to 서버
Client.destory() 실행
ctx.close 코드가 없다면 컨테이너의 종료과정을 수행치 않기 때문에 빈객체 소멸과정도 없다.
2.2 빈 객체의 초기화와 소멸 : 커스터 메서드
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 spring; public class Client2 { private String host; public void setHost(String host) { this.host = host; System.out.println("Client2.setHost() 실행"); } public void connect() throws Exception{ System.out.println("Client2.connect() 실행"); } public void send(){ System.out.println("Client2.send() 실행 "); } public void close() throws Exception{ System.out.println("Client2.close() 실행 "); } } | cs |
초기화과정에서 connect 소멸과정에서 close를 실행해야한다.
init-method 속성과 destroy-method 속성에 초기화와 소멸과정에서 사용될 메서드이름인 connect와 close를 지정하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?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="client" class="spring.Client"> <property name="host" value="서버" /> </bean> <bean id="client2" class="spring.Client2" init-method="connect" destroy-method="close"> <property name="host" value="서버2" /> </bean> </beans> | cs |
기존의 Main을 실행하면
Client.setHost() 실행
Client.afterPropertiesSet() 실행
Client2.setHost() 실행
Client2.connect() 실행
Client.send()to서버
3월 19, 2019 9:23:12 오전 org.springframework.context.support.GenericXmlApplicationContext doClose
정보: Closing org.springframework.context.support.GenericXmlApplicationContext@66a29884: startup date [Tue Mar 19 09:23:12 KST 2019]; root of context hierarchy
Client2.close() 실행
Client.destroy() 실행
init-method와 destroy-method 속성에서 지정한 메서드가 실행되는 것을 확인 할수 있다.
03 객체의 범위
Client client2 = ctx.getBean("client",Client.class);
client1==client2 -> true
이런 한 식별자에대해 한개의 객체만 존재하는 빈을 싱글톤 범위를 갖는다고 표현한다.
프로토 타입의 빈도 설정할 수있다.
빈의 범위를 프로토 타입으로 생성하면 매번 새로운 객체를 생성해서 리턴한다.
특정 빈을 프로토타입으로 지정하려면 <bean>의 scope속성 값을 prototype으로 지정하면된다.
주의점은 프로토타입의 빈은 빈의 완전한 라이프사이클을 따르지 않는다는 점이다.
스프링 컨테이너는 프로토타입의 빈을 생성하고 프로퍼티를 설정하고 초기화작업을 수행하지만
컨테이너를 종료한다고 해서 생성한 프로토타입의 빈객체의 소멸메서드를 실행하지는 않는다.
따라서 프로토 타입 범위의 빈을 사용할 때에는 빈 객체의 소멸 처리를 코드에서 직접 해야한다.