『 목차 』
- 컨테이너란? IoC/DI란? (퍼옴)
- 스프링 컨테이너 생명주기
- 빈 범위(Scope)
자바의 아버지, 제임스 고슬링 - 선 마이크로시스템즈라는 회사와 함께 자바라는 언어를 탄생시키는데 혁혁한 공을 세운 인물이며 SUN이 오라클로 인수된 뒤 얼마 지나지 않아 회사를 퇴임하고 현재 구글에서 근무 중이다. 개인적으로 C와 UNIX를 만든 데니스 리치 다음으로 훌륭한 프로그래머라고 생각한다.
스프링 혁명가, 로드 존슨 - 프로그래밍 계의 락스타같은 존재라 할 수 있다. 그가 한 유명한 말로 "항상 프레임워크 기반으로 접근하라"라는 말이 있는데 이 말은 곧 당신이 한 클래스에서 DB에 넣고 빼는 등 온갖 짓거리로 코드를 짜고 있다면 당신이 어디가서 프로그래밍의 프자도 꺼내서는 안된다는 말이다.
좀 더 구체적으로 DI에 대해 말하자면, DI방식으로 코드를 작성한다면 현재 사용하고 있는 객체의 메서드가 어떻게 작성되었는지 알 필요가 없으며 솔직히 그 클래스가 기능을 잘 구현했는지 조차도 알 필요가 없다. 그저 당신이 확인할 사항은 클래스의 기능을 추상적으로 묶어둔 인터페이스만 갖다 쓰면 그만이다. 나머지는 스프링에서 찜한 걸 연결시켜줄테니 말이다.
■ 스프링 컨테이너 생명주기
스프링 컨테이너 생명 주기는 다음과 같다.
스프링 컨테이너 생성
AbstractApplicationContext ctx = new GenericXmlApplicationContext();
스프링 컨테이너 설정 ( xml파일은 수정할 때마다 톰캣이 새로 파일을 읽어오기 때문에 refresh 필수! )
ctx.load("classpath:applicationCTX.xml");
ctx.refresh();
스프링 컨테이너 사용
Pencil pencil = ctx.getBean("pencil", Pencil.class);
스프링 컨테이너 종료
ctx.close();
여태까지 우리는 AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX.xml");
이렇게 선언했다. 이 코드는 스프링 컨테이너를 생성과 동시에 설정하는 방식이다.
그럼 이제 스프링 빈 생명주기에 대해 알아보자.
빈의 생명주기를 제어하는 방식은 3가지가 있다.
1) InitializingBean, DisposableBean 생명주기 인터페이스를 구현하고 초기화와 소멸작업을 위한 afterPropertiesSet()과 destroy() 메소드를 구현 (콜백함수)
-> 이러한 스프링에 종속된 인터페이스를 구현하면 빈이 스프링에 종속되므로 스프링 IoC 컨테이너 외부에서 재사용하지 못한다.
2) @PostConstruct, @PreDestroy 생명주기 어노테이션을 사용하여 초기화 및 소멸 콜백 메소드를 적용할 수 있다.
( 이 콜백 메소드를 호출하기 위해서는 IoC컨테이너에 CommonAnnotationBeanPostProcessor 인스턴스가 등록되어 있어야 한다? )
3) xml에서 Bean선언시 init-method와 destroy-method 속성을 설정해 콜백 메소드의 이름을 지정한다.
다음 예제는 위의 1,2번을 이용하여 빈의 생명주기를 제어하는 방식을 보여주는 예제이다. Student.java는 InitializingBean, DisposableBean을 implements해서 구현한 방식이고 AnnotationStudent.java는 Annotation을 사용해서 구현한 방식이다.
△ 예제 )
src/main/java
- com/student/ex
-MainClass.java
-Student.java
-AnnotationStudent.java
- com/main/resources
-applicationCTX.xml
[ Student ]
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 |
package com.student.ex;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Student implements InitializingBean, DisposableBean{
private String name;
private int age;
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("destroy()");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("afterPropertiesSet()");
}
} |
cs |
[ AnnotationStudent ]
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 |
package com.student.ex;
import javax.annotation.*;
public class AnnotationStudent {
private String name;
private int age;
public AnnotationStudent(String name, int age){
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@PostConstruct
public void initMethod(){
System.out.println("initMethod()");
}
@PreDestroy
public void destroyMethod(){
System.out.println("destroyMethod()");
}
} |
cs |
[ MainClass ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
package com.student.ex;
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:applicationCTX.xml");
ctx.refresh(); //xml파일을 수정하면 톰캣이 새로 파일을 읽어와야하기 때문에 refresh사용
ctx.close();
}
} |
cs |
[ applicationCTX ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
<?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 http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:annotation-config></context:annotation-config>
<bean id="student" class="com.student.ex.Student">
<constructor-arg value="홍길순"></constructor-arg>
<constructor-arg value="30"></constructor-arg>
</bean>
<bean id="annotationstudent" class="com.student.ex.AnnotationStudent">
<constructor-arg value="홍길자"></constructor-arg>
<constructor-arg value="40"></constructor-arg>
</bean>
</beans> |
cs |
applicationCTX에서 context:annotation-config를 추가한 부분은 AnnotationStudent를 연결해 주기 위해 있는 코드야!
출력 결과 및 분석
afterPropertiesSet()
initMethod()
destroyMethod()
destroy()
- 콜백함수란? 시스템에서 내부적으로 어떤 이벤트가 발생할때 자동으로 호출해주는 함수
- Student 객체에서 InitializingBean, DisposableBean를 구현했기 때문에 콜백함수 afterPropertiesSet(), initMethod()가 자동으로 호출된것!
- AnnotationStudent 객체에서 생명주기 어노테이션 @PostConstruct , @PreDestroy을 구현했기 때문에 initMethod()와 destroy()메소드를 호출 ( 어노테이션으로 찾아가기때문에 메소드 이름이 initMethod()가 아니더라도 호출됨 )
■ 스프링 빈 범위(Scope)
스프링에서 빈 범위는 총 6가지가 있다. 기본적으로 지정을 안해주면 singleton으로 지정이된다.
1) singleton
2) prototype
3) request
4) session
5) page
6) application
1,2번은 빈즈 기반, 3~6번은 웹 기반 scope이다.
여기서 singleton과 prototype의 차이점!!
다음과 같은 예제에서 bean설정을 해주는 xml파일을 주목하자!
△ 예제 )
src/main/java
- com/scope/ex
-MainClass.java
-Student.java
- com/main/resources
-applicationCTX2.xml
이 예제에서 보면 MainClass에서 Student객체의 레퍼런스 student1과 student2는 applicationCTX2.xml에서 getBean으로 받아오고 있고, applicationCTX2.xml를 보면 Bean을 한번만 설정하고 있어서 MainClass에서 받아오는 student1과 student2는 구조적으로 하나의 객체를 공유하고 있다.
[ Student ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
package com.scope.ex;
public class Student {
private String name;
private int age;
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
} |
cs |
[ MainClass ]
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 |
package com.scope.ex;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX2.xml");
Student student1 = ctx.getBean("student", Student.class);
System.out.println("이름 : "+student1.getName());
System.out.println("나이 : "+student1.getAge());
System.out.println("===============================");
Student student2 = ctx.getBean("student", Student.class);
student2.setName("홍길자");
student2.setAge(100);
System.out.println("이름 : "+student2.getName());
System.out.println("나이 : "+student2.getAge());
System.out.println("===============================");
if(student1.equals(student2)){
System.out.println("student1 == student2");
}else{
System.out.println("student1 != student2");
}
ctx.close();
}
} |
cs |
[ applicationCTX2 ]
1
2
3
4
5
6
7
8
9
10
11 |
<?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="student" class="com.scope.ex.Student" scope="prototype">
<constructor-arg value="홍길순"></constructor-arg>
<constructor-arg value="30"></constructor-arg>
</bean>
</beans> |
cs |
위의 코드의 결과는 아래와 같다.
왜 이런결과가 나올까?
xml에서 Bean은 객체를 한번만 가져왔고, MainClass에서 이 하나의 Bean을 student1, student2로 레퍼런스 두개를 생성해서 하나의 객체를 참조했는데 왜 student1.equals(student2)의 결과가 student != student2가 나올까? 하나의 객체인데?
정답은 applicationCTX2의 6번 줄에서 bean의 scope를 지정하는 부분!! 이부분이 핵심이다 !!!!
위에서 언급했듯이 scope를 지정하지 않으면 기본적으로 singleton으로 지정되지만, 위의 코드에서는 prototype으로 지정했다. 여기서 prototype과 singleton의 차이점을 알 수 있다.
자바에서
Student stu1 = new Student();
Student stu2 = new Student();
if(stu1.equals(stu2)){
System.out.println(true);
}else{
System.out.println(false);
}
위의 코드는 false를 반환한다. 객체 student가 두개 생성되어 stu1, stu2는 각각의 객체를 참조한다.
이러한 방식은 prototype이다.
Student stu1 = new Student();
Student stu2 = stu1;
if(stu1.equals(stu2)){
System.out.println(true);
}else{
System.out.println(false);
}
하지만 위의 코드는 객체 student가 하나만 생성되어 stu1, stu2가 이 하나의 객체를 참조하기 때문에 true를 반환한다.
이러한 방식이 바로 singleton이다.
따라서 위의 예제에서 scope="singleton"으로 지정해주거나 또는 아예 scope를 지정해주지 않았다면 처음 우리가 예상했던 것처럼 하나의 객체를 공유하기 때문에 student1 == student2 결과가 출력되었을 것이다.
하지만 scope을 prototype으로 지정해줌으로써 applicationCTX2.xml에서 bean을 하나만 지정해주었지만 객체는 두개가 생성되었기 때문에
student1 != student2라는 결과가 나온 것이다.
'Web > Spring' 카테고리의 다른 글
Spring MVC(1) (0) | 2016.05.16 |
---|---|
Spring 한글처리 (0) | 2016.05.16 |
Spring(4)_Environment,Properties (0) | 2016.05.13 |
Spring(2) (0) | 2016.05.11 |
Spring(1) (0) | 2016.05.10 |