1. Bean의 생명주기
bean의 생명 주기에서 특정 시기에 추가적인 동작을 시키고 싶다면 어떻게 하면 될까?
1) 인터페이스 방법
InitializingBean, DisposableBean 인터페이스를 상속해서 afterPropertiesSet(), destroy() 메소드를 오버라이딩 하면 두 메소드는 각각 bean 초기화 과정, bean 소멸 과정에서 호출된다.
Context 클래스에서 close() 메소드로 컨테이너를 닫지 않는다면 destory() 메소드는 호출 되지 않는다.
package com.example.demo;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Programmer implements InitializingBean, DisposableBean{
private String name;
private int age;
private int codeLevel;
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;
}
public int getCodeLevel() {
return codeLevel;
}
public void setCodeLevel(int codeLevel) {
this.codeLevel = codeLevel;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy()");
}
}
3개의 Programmer 오브젝트 생성 및 해제
2) @(Annotation) 방법
@PostConstruct, @PreDestroy 어노테이션을 각각 bean 초기화 과정, bean 소멸 과정에서 호출할 메소드 앞에 붙여주는 방법이다.
Context 클래스에서 close() 메소드로 컨테이너를 닫지 않는다면 destoryMethod() 메소드는 호출 되지 않는다.
package com.example.demo;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Programmer {
private String name;
private int age;
private int codeLevel;
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;
}
public int getCodeLevel() {
return codeLevel;
}
public void setCodeLevel(int codeLevel) {
this.codeLevel = codeLevel;
}
@PostConstruct
public void initMethod() {
System.out.println("initMethod()");
}
@PreDestroy
public void destroyMethod() {
System.out.println("destroyMethod()");
}
}
3개의 Programmer 오브젝트 생성 및 해제
3) Bean의 scope
- Spring이 관리하는 bean의 관리 범위를 의미하며, 하나의 scope 안에서는 매번 같은 오브젝트를 가져온다.
- scope 속성을 생략한 bean은 기본적으로 싱글톤으로 관리가 되는데, 굳이 표시하고 싶다면 scope를 singleton으로 지정하면 된다. 싱글톤은 최대 하나만 존재할 수 있는 객체이다.
<bean id="..." class="..." scope="singleton">
...
</bean>
또는
@Component
@Scope("singleton")
public class ... {}
- singleton 외의 scope로는 다음과 같은 종류들이 있다.
- prototype : https://kimcoder.tistory.com/464 참고
- request : 하나의 웹 요청 안에서 만들어지고 해당 요청이 끝날 때 제거된다.
- session : HTTP session과 같은 존재 범위를 갖는다. 로그인 정보나 사용자의 선택 옵션을 저장해두기에 유용하며, HTTP session에 저장되는 정보를 모든 계층에서 안전하게 이용할 수 있다.
- globalSession : Portlet에만 존재하는 글로벌 세션에 저장된다. Portlet은 재사용 가능한 웹 구성요소로서 날씨, 토론방, 뉴스같이 포탈 사용자에게 관련 정보나 지식을 보여주는 메뉴를 의미한다.
- application : 웹 애플리케이션마다 만들어지는 서블릿 컨텍스트에 저장된다. 그래서 singleton scope와 존재 범위가 비슷하지만, 웹 애플리케이션과 애플리케이션 컨텍스트의 존재 범위가 다른 경우가 있기 때문에 singleton scope와 따로 구분한다. 그리고 singleton scope와 마찬가지로 상태를 갖지 않거나 읽기전용이 되어야 한다.
※ prototype, request, session, globalSession은 독립적인 상태를 저장해두고 사용하는 데 필요하다.
- 스프링 웹 플로우나 제이보스 씸과 같은 프레임워크를 이용하여 Scope 인터페이스를 구현한 다양한 커스텀 scope를 제공받을 수 있다.
2. Properties 파일
1) .properties 파일이란?
Spring 프로젝트를 생성했을 때 src/main/resources 경로에 생기는 .properties 파일의 용도는 무엇일까?
- Spring에서는 bean 설정 작업에 필요한 property 정보를 컨테이너가 관리하고 제공해주는데, 컨테이너는 지정된 정보 소스로부터 프로퍼티 값을 수집하여 이를 bean 설정 작업에 참고할 수 있게 해준다. 컨테이너가 프로퍼티 값을 가져오는 대상을 property source라고 한다.
- 기본적으로 ISO-8859-1 인코딩을 지원한다.
2) Environment 객체
- .properties 파일로부터 가져오는 프로퍼티 값은 컨테이너가 관리하는 Environment 오브젝트에 저장된다.
- @Autowired를 통해 필드로 주입받을 수 있다.
- Environment 객체의 getProperty() 메소드를 이용하여 프로퍼티 값을 불러올 수 있다.
예시
이 예제에서는 JAVA 소스코드상에서 properties 파일의 속성값들을 불러오고, 이 속성값들을 Bean에 넣어 외부에서 Bean 객체를 생성해보기로 한다.
src/main/resource 아래에 2개의 properties 파일을 만들어보았다.
작성법은 간단하다. "속성=속성값" 형태를 줄을 경계로 나열하면 된다.
관리자가 2명인 환경이라고 보면 좋을 듯하다.
<jooyeok.properties>
infoId_1=jooyeok
infoPw_1=07031
<lamb.properties>
infoId_2=lamb
infoPw_2=01051
<InfoConnection.java>
- EnvironmentAware 인터페이스를 상속해서 setEnvironment 메소드를 오버라이드 한 것이 특징이다.
- setEnvironment 메소드는 afterPropertiesSet 메소드보다도 일찍 호출되며, Environment 객체를 setter 메소드로 미리 지정해두는 역할을 한다.
- afterPropertiesSet 메소드에서는 setter 메소드들을 사용해서 properties 파일의 속성값들을 Bean에 적용해준다.
package com.example.demo;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
public class InfoConnection implements EnvironmentAware, InitializingBean, DisposableBean {
private Environment env;
private String infoId_1;
private String infoPw_1;
private String infoId_2;
private String infoPw_2;
@Override
public void setEnvironment(Environment env) {
setEnv(env);
}
public Environment getEnv() {
return env;
}
public void setEnv(Environment env) {
this.env = env;
}
//setter, getter 생략
@Override
public void afterPropertiesSet() throws Exception {
setInfoId_1(env.getProperty("infoId_1"));
setInfoPw_1(env.getProperty("infoPw_1"));
setInfoId_2(env.getProperty("infoId_2"));
setInfoPw_2(env.getProperty("infoPw_2"));
}
@Override
public void destroy() throws Exception {
System.out.println("destroy()");
}
}
<application.xml>
- id가 infoConnection인 InfoConnection Bean 객체를 생성한다.
<?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="infoConnection" class="com.example.demo.InfoConnection"/>
</beans>
<DemoApplication.java>
package com.example.demo;
import java.io.IOException;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.support.ResourcePropertySource;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// Part 1 - properties들을 env에 등록
ConfigurableApplicationContext ctx = new GenericXmlApplicationContext();
ConfigurableEnvironment env = ctx.getEnvironment();
MutablePropertySources mps = env.getPropertySources();
try {
mps.addLast(new ResourcePropertySource("classpath:jooyeok.properties"));
mps.addLast(new ResourcePropertySource("classpath:lamb.properties"));
// env.getProperty("infoId_1") -> "jooyeok" 출력
} catch (IOException e) {
e.getStackTrace();
}
// Part 2 - env에 등록된 내용들이 저장되어 있는 infoConnection Bean을 호출
GenericXmlApplicationContext gCtx = (GenericXmlApplicationContext)ctx;
gCtx.load("application.xml");
gCtx.refresh();
InfoConnection infoConnection = gCtx.getBean("infoConnection",InfoConnection.class);
// infoConnection.getInfoId_1(); -> "jooyeok 출력
// infoConnection.getInfoPw_1(); -> "07031" 출력
// infoConnection.getInfoId_2(); -> "lamb" 출력
// infoConnection.getInfoPw_2(); -> "01051" 출력
ctx.close();
gCtx.close();
}
}
※ @PropertySource("[.properties 파일]") 어노테이션을 클래스에 추가하여 리소스를 등록하는 방법도 있다.
Part 1
- ConfigurableApplicationContext는 최상위 Context 클래스이다. 이 컨텍스트 객체에서 getEnvironment() 메소드로 Environment 객체를 가져왔으면 properties 리소스들을 Environment 객체의 PropertySource에 추가한다. 이렇게 해서 Environment 객체에는 properties 파일들의 모든 속성값이 담기게 되었다.
- 최종적으로 Environment 객체의 getProperty("[속성명]") 으로 속성값을 반환하여 JAVA 소스코드상에서 properties 파일의 속성값을 불러올 수 있게 된 것이다.
- main문에서 실행되는 것이기 때문에 일단은 동작하지만, 실제 웹 환경에서는 ApplicationContextInitializer 인터페이스의 initialize 메소드를 오버라이딩하여 그 안에 part 1 코드를 작성하고, 이 컨텍스트 초기화 클래스를 web.xml에 다음과 같이 <context-param> 또는 <init-param>의 contextInitializerClasses 파라미터로 지정해주면 된다.
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>MyContextInitializer</param-value>
</context-param>
Part 2
- GenericXmlApplicationContext 클래스의 load() 메소드를 사용하기 위해 ctx를 형변환하여 gCtx라는 이름으로 사용하겠다고 선언한뒤, load() 메소드로 application.xml 파일을 받아서 Bean 객체를 불러온다.
- load후에는 컨테이너 설정을 새로고침하는 refresh() 메소드를 필수적으로 호출해야 한다.
'Spring Series > Spring Framework' 카테고리의 다른 글
[Spring] profile 속성 / AOP(1) - xml 이용 (0) | 2020.12.28 |
---|---|
[Spring] properties 파일(2) (0) | 2020.12.23 |
[Spring] IOC 컨테이너 설정(3) - xml, @(Annotation) 혼용 (0) | 2020.12.22 |
[Spring] IOC 컨테이너 설정(2) - @(Annotation) (0) | 2020.12.21 |
[Spring] DI와 IOC / IOC 컨테이너 설정(1) - xml (0) | 2020.12.18 |
댓글