DevLog ๐ถ
[Spring] ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ, ๋น ์ค์ฝํ ๋ณธ๋ฌธ
- ๊น์ํ ๋์ '์คํ๋ง ํต์ฌ์๋ฆฌ - ๊ธฐ๋ณธํธ'์ ๋ณด๊ณ ์ ๋ฆฌํ ๊ธ์ ๋๋ค ๐
- ์ง๋ ํฌ์คํ ๊ณผ ์ด์ด์ง๋๋ค :D
- ์คํ๋ง ํต์ฌ์๋ฆฌ ๊ฐ์์ ๋ง์ง๋ง ์ ๋ฆฌ๊ฐ ๋ ๊ฒ ๊ฐ์ต๋๋ค ๐คญ
| ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ
- DB ์ปค๋ฅ์ ํ ๊ฐ์ ์์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ ์ ํ์ํ ์ฐ๊ฒฐ์ ๋ฏธ๋ฆฌ ํด๋๊ณ , ์ข ๋ฃ ์์ ์ ํ ๋ฒ์ ์ข ๋ฃํ๋ ๊ฒ ๋ซ๋ค.
- ์คํ๋ง์์๋ ์ด๋ฐ ์ด๊ธฐํ ๋ฐ ์ข ๋ฃ ์์ ์ ์ด๋ป๊ฒ ํ ๊น?
- ๊ฐ๋จํ ์์ ์ฝ๋๋ฅผ ํตํด์ ์์๋ณด์.
[NetworkClient.java]
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("์์ฑ์ ํธ์ถ, url = " + url);
connect();
call("์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง");
}
public void setUrl(String url) {
this.url = url;
}
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message: " + message);
}
public void disconnect() {
System.out.println("close: " + url);
}
}
โ Test
[BeanLifeCycleTest.java]
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest() throws Exception {
ConfigurableApplicationContext ac
= new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient networkClient = ac.getBean(NetworkClient.class);
ac.close(); // ์คํ๋ง ์ปจํ
์ด๋ ์ข
๋ฃ
}
@Configuration
static class LifeCycleConfig {
@Bean
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hi.dev");
return networkClient;
}
}
}
โ ์คํ ๊ฒฐ๊ณผ
์์ฑ์ ํธ์ถ, url = null
connect: null
call: null message: ์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง
- ๋ถ๋ช ๊ธฐ๋ํ ๊ฑด setUrl์ ํตํด ๋ฃ์ด์ง url ๊ฐ์ด ๋์์ผ ํ๋๋ฐ, null์ด ์ฐํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
- ์ด๋ ๋น์ฐํ๋ค. ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋จ๊ณ์์๋ url์ด ์ด๊ธฐํ๋์ง ์์ ์ํ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ์ฐ๋ฆฌ๋ ์์ฑ๋ ์ดํ ์์ ์๋ก url์ ์ฃผ์ ํ์๋ค.
url์ ๋ด์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๊ณ ์ถ๋ค๋ฉด ์ธ๋ถ์์ networkClient.connect() ํน์ .call()์ ํธ์ถํด์ผ ์ฐํ๊ฒ ๋๋ค.
๐ฉ ์ฌ์ค, ์์ ์์ setUrl()์ ํ๋ ๊ฒ ๊ณผ์ฐ '์์กด๊ด๊ณ ์ฃผ์ '์ ๊ด์ ์์ ๋ณผ ์ ์์๊น... ๋ผ๋ ์๋ฌธ์ด ๋ค ์ ์๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก '์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ํ๋ค' ๋ผ๋ ๊ด์ ์์ ๋ณด๋ฉด, url์ ์ธ๋ถ๋ก๋ถํฐ ์ฃผ์ ๋ฐ๊ธฐ ๋๋ฌธ์ ์์กด๊ด๊ณ ์ฃผ์ ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
๊ทธ๋ฌ๋, DI๋ฅผ ์ํํ๋ ๊ถ๊ทน์ ์ธ ์ด์ ๋ '์ถ์ํ๋ ๊ฒ์ ์์กด'ํ์ฌ '๊ฒฐํฉ๋'๋ฅผ ๋ฎ์ถ๊ธฐ ์ํจ์ด๋ค.
์ ์์ ์์ setUrl()์ ์งํํ ๋ url์ ํ์ ์ String์ด์ด์ ๋ณ๋์ ์ธํฐํ์ด์ค ํ์ ์ด ์๋๊ธฐ ๋๋ฌธ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ค๋ ๊ด์ ์์ ๋ณด๋ฉด ์์กด๊ด๊ณ ์ฃผ์ ์ด๋ผ๊ณ ๋ณด๊ธฐ๋ ์ด๋ ต๋ค. (์ด ๋ถ๋ถ ์ฐพ์๋ณด๋ฉด์ ๊ฐ์ธ์ ์ผ๋ก DI์ ๋ํด ๋ค์ ์๊ฐํด๋ณผ ์ ์์๋ค.)
โญ ์คํ๋ง ๋น์ ๊ฐ์ฒด ์์ฑ -> ์์กด๊ด๊ณ ์ฃผ์ ์์ผ๋ก ์ผ์ด๋๋ค.
- ์ฆ, ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ ์์กด๊ด๊ณ ์ฃผ์ ์ด ๋๋์ผ๋ง ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด๊ธฐํ๋ ๊ทธ ์ดํ์ ์งํ๋์ด์ผ ํ๋ค.
- ์คํ๋ง์ ์์กด๊ด๊ณ ์ฃผ์ ์๋ฃ ์, ์คํ๋ง ๋น์๊ฒ ์ฝ๋ฐฑ ๋ฉ์๋๋ฅผ ํตํด ์ด๊ธฐํ ์์ ์ ์๋ ค์ฃผ๋ฉฐ, ์๋ฉธ ์ ์๋ ์๋ฉธ ์ฝ๋ฐฑ์ ๋ ๋ฆฐ๋ค.
- InitializingBean, DisposableBean Interface
- ์ค์ ์ ๋ณด์ ์ด๊ธฐํ ๋ฉ์๋, ์ข ๋ฃ ๋ฉ์๋ ์ง์
- @PostConstruct, @PreDestroy
์ฆ, ๋ค์๊ณผ ๊ฐ์ ๋ผ์ดํ ์ฌ์ดํด์ ๊ฐ์ง๋ค๊ณ ๋ณผ ์ ์๋ค.
๐ฉ ์คํ๋ง ์ปจํ ์ด๋ ์์ฑ -> ์คํ๋ง ๋น ์์ฑ -> ์์กด๊ด๊ณ ์ฃผ์ -> ์ด๊ธฐํ ์ฝ๋ฐฑ -> (์ค์ ์ฌ์ฉ) -> ์๋ฉธ ์ ์ฝ๋ฐฑ -> ์คํ๋ง ์ข ๋ฃ
- โญ ๋จ, ์์ฑ์ ์ฃผ์ ์์๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ์คํ๋ง ๋น์ด ํจ๊ป ๋ค์ด์์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด ์์ฑ ๋ฐ ์์กด๊ด๊ณ ์ฃผ์ ์ด ๋์์ ์ผ์ด๋๋ค.
- ๊ฐ์ฒด ์์ฑ ์ ์์ฑ์์ ํ๋ผ๋ฏธํฐ๊ฐ ์กด์ฌํ๋ค๋ฉด, ํด๋น ํ๋ผ๋ฏธํฐ์๋ ์ด๋ฏธ ๋ง๋ค์ด์ง ์คํ๋ง ๋น์ด ๋ค์ด์์ผ ์ฃผ์ ํ ์ ์์ผ๋๊น.
- ์ฐธ๊ณ ๋ก, ์ ์ง๋ณด์ ๊ด์ ์์๋ ์์ฑ์ ๋ด๋ถ์์ ์ด๊ธฐํ๋ฅผ ์งํํ๋ ๊ฑด ๋ณ๋ก ์ข์ง ์๋ค.
์์ฑ์๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ์ด๊ธฐํํ๋ ๋ถ๋ถ์ ๋ฐ๋ก ๋๋์ด์ผ ์ ์ง๋ณด์ ์ ๋ ๋ซ๋ค. (๊ฐ๋จํ ์์ ์ด๋ผ๋ฉด ์์ฑ์ ๋ด์์ ํด๋ ๋๊ฒ ์ง๋ง)
| ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ - InitializingBean, DisposableBean
- ์์์ ์ฌ์ฉํ์๋ NetworkClient ์์ ๋ฅผ ์์ ํด๋ณด์.
[NetworkClient.java]
public class NetworkClient implements InitializingBean, DisposableBean {
private String url;
public NetworkClient() {
System.out.println("์์ฑ์ ํธ์ถ, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message: " + message);
}
public void disconnect() {
System.out.println("close: " + url);
}
@Override
public void afterPropertiesSet() throws Exception {
connect();
call("์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง");
}
@Override
public void destroy() throws Exception {
disconnect();
}
}
- InitializingBean, DisposableBean ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ฌ afterPropertiesSet(), destroy() ํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉ ํ์๋ค.
- ๊ธฐ์กด์ ์์ฑ์์์ ์งํํ์๋ connect, call ์์ ์ afterPropertiesSet()์์ ์งํํ๋๋ก ๋ณ๊ฒฝํ์๋ค.
- ๊ฐ๊ฐ์ ์ด๊ธฐํ, ์๋ฉธ์ ์ง์ํ๋ ๋ฉ์๋๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค!
โ ์คํ ๊ฒฐ๊ณผ
์์ฑ์ ํธ์ถ, url = null
connect: http://hi.dev
call: http://hi.dev message: ์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง
17:31:45.756 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cdc4c27, started on Sun Aug 07 17:31:45 KST 2022
close: http://hi.dev
- ํ ์คํธ๋ฅผ ์คํํด๋ณด๋ฉด ์ฑ๊ณต์ ์ผ๋ก url์ด ์ ๋ค์ด๊ฐ ๊ฑธ ๋ณผ ์ ์๋ค.
- ์ด๋ ๊ฐ ์ฃผ์ ์ดํ(setUrl) ๊ฐ ํจ์๊ฐ ํธ์ถ๋๊ณ ์๋ฉธ๋์๊ธฐ ๋๋ฌธ์ด๋ค.
- ๋จ, ์ ๋ฐฉ๋ฒ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์คํ๋ง ์ธํฐํ์ด์ค์ ์์กดํ๋ฉฐ, ์ด๊ธฐํ/์๋ฉธ ๋ฉ์๋ ์ด๋ฆ์ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํ๋ค.
- ์ฌ์ค์, ์ ๋ฐฉ๋ฒ์ ๊ฑฐ์ ์ฌ์ฉํ์ง ์๋๋ค๊ณ ๋ด๋ ๋ฌด๋ฐฉํ๋ค.
๐ฉ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ข ์์ ์ด๋ฉด ๋ฌด์์ด ์ ์ข์๊น?
- ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ๋๋ฉด JVM์ ์ํด์ .class -> .java๋ก ๋ณ๊ฒฝ์ด ๋๋๋ฐ, .class์ ๊ฒฝ์ฐ ์ปดํ์ผ์ด ๋์ด๋ฒ๋ฆฐ ์ํ์ด๊ธฐ ๋๋ฌธ์ ์์ ์ด ์ด๋ ต๋ค.
๊ฐ๋ฐ์์ ์ ์ฅ์์๋ ์์ฑํ ์์ค์ฝ๋๋ฅผ ์์ ํ์ง๋ง, ์ค์ ๋ก ํ๋ก๊ทธ๋จ์ด ๋์ํ ๋๋ ์์ค์ฝ๋๋ฟ๋ง ์๋๋ผ ์ฌ๋ฌ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํจ๊ป ์ฌ์ฉํ๋๋ฐ,
์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ด๋ฏธ .class๋ก ์ปดํ์ผ์ด ๋์ด ์๋ ์ํ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ์์ ํ ์ ์๋ค.
| ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ - ์ด๊ธฐํ, ์๋ฉธ ๋ฉ์๋ ์ง์
- ๋น ๋ฑ๋ก ์ ์ค์ ์ ๋ณด์ initMethod, destroyMethod๋ฅผ ์ง์ ํ ์ ์๋ค. ๋ค์ ์์ ๋ฅผ ์์ ํด๋ณด์.
[NetworkClient.java]
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("์์ฑ์ ํธ์ถ, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message: " + message);
}
public void disconnect() {
System.out.println("close: " + url);
}
public void init() {
System.out.println("NetworkClient.init");
connect();
call("์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง");
}
public void close() {
System.out.println("NetworkClient.close");
disconnect();
}
}
[BeanLifeCycleTest.java]
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest() throws Exception {
ConfigurableApplicationContext ac
= new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient networkClient = ac.getBean(NetworkClient.class);
ac.close(); // ์คํ๋ง ์ปจํ
์ด๋ ์ข
๋ฃ
}
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hi.dev");
return networkClient;
}
}
}
- @Bean์ initMethod์ destroyMethod๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค. ์ฐธ๊ณ ๋ก, ์ด๋ฆ์ ์๋ชป ์ ๋ ฅํ๋ฉด ์ปดํ์ผ ์์ ์ ์๋ฌ๋๋ค. (์ ๊ธฐ...!)
โ ์คํ ๊ฒฐ๊ณผ
์์ฑ์ ํธ์ถ, url = null
NetworkClient.init
connect: http://hi.dev
call: http://hi.dev message: ์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง
17:53:36.244 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cdc4c27, started on Sun Aug 07 17:53:35 KST 2022
NetworkClient.close
close: http://hi.dev
- ๊ฒฐ๊ณผ๊ฐ ์ ๋์ค๋ ๊ฑธ ๋ณผ ์ ์๋ค. ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ง ์์๋ ๋๋ฉฐ, ๋ฉ์๋ ์ด๋ฆ์ ๋ณ๊ฒฝํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค!
- ํนํ, destroyMethod์ ๊ฒฝ์ฐ, default ๊ฐ์ด (inferred)์ด๊ธฐ ๋๋ฌธ์ close(), shutdown()์ด๋ผ๋ ์ด๋ฆ์ ๋ฉ์๋๋ฅผ ์๋ ํธ์ถํ๋ค.
- ๊ทธ๋์ ์ ์์ ์์๋ ๋ฐ๋ก destroyMethod๋ฅผ ์ง์ ํ์ง ์์๋ ์ ๋์ํ๋ค :D
๐ฉ ์ถ๊ฐ์ ์ผ๋ก, ์ ๋ฐฉ๋ฒ์ @Bean์์๋ง ์ ์ฉ์ ํ ์ ์๋ค. ์๋ ๋ฑ๋กํ๋ @Component์์๋ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํ๋ค.
๊ทธ๋์ ๋ณดํต ์ธ ๋ฒ์งธ๋ก ๋์ค๋ ๋ฐฉ๋ฒ์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ค :)
| ๋น ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ - @PostConstruct, @PreDestroy
- ๊ฐ์ธ์ ์ผ๋ก ๊ฐ์ฅ ์ ํธํ๋ ๋ฐฉ๋ฒ์ด๋ค. ์๋ง ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ์ง ์์๊น...
[NetworkClient.java]
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("์์ฑ์ ํธ์ถ, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
public void connect() {
System.out.println("connect: " + url);
}
public void call(String message) {
System.out.println("call: " + url + " message: " + message);
}
public void disconnect() {
System.out.println("close: " + url);
}
@PostConstruct
public void init() {
System.out.println("NetworkClient.init");
connect();
call("์ด๊ธฐํ ์ฐ๊ฒฐ ๋ฉ์์ง");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disconnect();
}
}
- ๋ค์๊ณผ ๊ฐ์ด @PostConstruct, @PreDestroy๋ฅผ ๋ถ์ฌ์ฃผ๋ฉด ๋๋ค.
- ํ ์คํธ ์ฝ๋์ @Bean๋ ์๋๋๋ก ๋๋ ค๋์. ์ ๋์๊ฐ๋ค.
- ์ ๋ฐฉ๋ฒ์ ๊ฒฝ์ฐ ํด๋น ์ด๋ ธํ ์ด์ ์ ์๋ฐ ํ์ค์ด๊ธฐ ๋๋ฌธ์ ์คํ๋ง์ ์ข ์์ ์ด์ง ์๋ค๋ ํน์ง์ด ์๋ค.
- ๊ทธ๋ฌ๋, ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์ ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ (๊ฐ๋ฐ์๊ฐ ์ง์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฝ๋๋ฅผ ์์ ํ ์ ์์ผ๋๊น)
๊ทธ๋๋ @Bean์ initMethod๋ฅผ ์ฌ์ฉํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
| ๋น ์ค์ฝํ
- ์คํ๋ง ๋น์ ๊ธฐ๋ณธ์ ์ผ๋ก ์คํ๋ง ์ปจํ ์ด๋์ ์์๊ณผ ์ข ๋ฃ๊น์ง ์ ์ง๋๋ค. ์ด๋ ์ฑ๊ธํค ์ค์ฝํ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
๐ฉ ์ฑ๊ธํค์ด๊ธฐ ๋๋ฌธ์ ์ปจํ ์ด๋์์ ํญ์ ๊ฐ์ ์ธ์คํด์ค์ ์คํ๋ง ๋น์ ๋ฐํํ๋ ๊ฒ์ด๋ค.
- ์ฑ๊ธํค ์ธ์๋ ๋ค์ํ ์ค์ฝํ๊ฐ ์กด์ฌํ๋ค.
- ์ฑ๊ธํค: ๊ธฐ๋ณธ, ์คํ๋ง ์ปจํ ์ด๋์ ์์๊ณผ ์ข ๋ฃ๊น์ง ์ ์ง
- ํ๋กํ ํ์ : ํ๋กํ ํ์ ๋น์ ์์ฑ ๋ฐ ์์กด๊ด๊ณ๊น์ง๋ง ๊ด์ฌํ๊ณ ๊ด๋ฆฌ X, ๋งค์ฐ ์งง๋ค
- ์น (request) : ์น ์์ฒญ์ด ๋ค์ด์ค๊ณ ๋๊ฐ ๋๊น์ง
- ์น (session) : ์น ์ธ์ ์ด ์์ฑ๋๊ณ ์ข ๋ฃ๋ ๋๊น์ง
- ์น (application) : ์๋ธ๋ฆฟ ์ปจํ ์คํธ์ ๊ฐ์ ๋ฒ์
- ์ค์ฝํ๋ @Scope("prototype")๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ง์ ๊ฐ๋ฅํ๋ค.
| ๋น ์ค์ฝํ - ํ๋กํ ํ์ ์ค์ฝํ
ํ๋กํ ํ์ ์ค์ฝํ๋ฅผ ์กฐํํ๋ฉด ์คํ๋ง ์ปจํ ์ด๋๋ ํญ์ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ๋ฐํํด์ค๋ค.
๋ง์ฝ, ํด๋ผ์ด์ธํธ๊ฐ ํ๋กํ ํ์ ์ค์ฝํ ๋น์ ์์ฒญํ๋ฉด, ์คํ๋ง ์ปจํ ์ด๋๋ ํด๋น ์์ ์ ํ๋กํ ํ์ ๋น ์์ฑ + DI๊น์ง ์งํํ๋ค.
์ดํ, ์์ฑํ ๋น์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ฉฐ, ์คํ๋ง ์ปจํ ์ด๋์์๋ ๋ ์ด์ ๊ด๋ฆฌํ์ง ์๋๋ค. ์์ฒญ์ด ์ฌ ๋๋ง๋ค ์๋ก ์ ๊ณผ์ ์ ์งํํ๋๊น.
โญ ์คํ๋ง ์ปจํ ์ด๋๋ ๋น ์์ฑ, ์์กด๊ด๊ณ ์ฃผ์ , ์ด๊ธฐํ๊น์ง๋ง ๊ด์ฌํ๊ธฐ ๋๋ฌธ์ @PreDetroy ๊ฐ์ ๋ฉ์๋๋ ํธ์ถ๋์ง ์๋๋ค!
์คํ๋ง ์ปจํ
์ด๋ ์์ฑ -> ์คํ๋ง ๋น ์์ฑ -> ์์กด๊ด๊ณ ์ฃผ์
-> ์ด๊ธฐํ ์ฝ๋ฐฑ -> (์ค์ ์ฌ์ฉ) -> ์๋ฉธ ์ ์ฝ๋ฐฑ -> ์คํ๋ง ์ข
๋ฃ
โ Test
[PrototypeTest.java]
public class PrototypeTest {
@Test
public void prototypeBeanFind() throws Exception {
AnnotationConfigApplicationContext ac
= new AnnotationConfigApplicationContext(PrototypeBean.class);
System.out.println("find bean 1");
PrototypeBean bean1 = ac.getBean(PrototypeBean.class);
System.out.println("find bean 2");
PrototypeBean bean2 = ac.getBean(PrototypeBean.class);
System.out.println("bean1 = " + bean1);
System.out.println("bean2 = " + bean2);
assertThat(bean1).isNotSameAs(bean2);
ac.close();
}
@Scope("prototype")
static class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
}
โ ์คํ ๊ฒฐ๊ณผ
find bean 1
PrototypeBean.init
find bean 2
PrototypeBean.init
bean1 = study.springstudy.lifecycle.PrototypeTest$PrototypeBean@293bb8a5
bean2 = study.springstudy.lifecycle.PrototypeTest$PrototypeBean@2416a51
18:36:32.993 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1cdc4c27, started on Sun Aug 07 18:36:32 KST 2022
์ฌ๊ธฐ์ ์ ์ ์๋ ์ฌ์ค 3๊ฐ์ง!
1) @PostConstruct๋ ์ํ๋์๋ค. ์ฆ, ์คํ๋ง ์ปจํ ์ด๋์์ ๋น์ ์กฐํํ ๋ ์์ฑ ๋ฐ ์ด๊ธฐํ ๋ฉ์๋๊น์ง ์คํ๋๋ค.
2) ์กฐํ ์ ๊ฐ๊ฐ ๋ค๋ฅธ ๊ฐ์ฒด๊ฐ ์กฐํ๋ ๊ฑธ ๋ณผ ์ ์๋ค. ๊ณ์ ์๋กญ๊ฒ ์์ฑํจ์ ์ ์ ์๋ค.
3) @PreDestroy๋ ์คํ๋์ง ์์๋ค. ์ด๊ธฐํ๊น์ง๋ง ๊ด์ฌํ๋๊น ๊ทธ ์ดํ๋ ๊ด๋ฆฌํ์ง ์๋ ๊ฒ์ด๋ค.
๊ทธ๋์, ํด๋ผ์ด์ธํธ๊ฐ ์ง์ ์ข ๋ฃ ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ํ๋ค.
๋ฌผ๋ก , ์ข ๋ฃํ์ง ์๋๋ผ๋ ์ด์จ๊ฑฐ๋ ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ์ GC์ ์ํด์ Heap ์์ญ ์ ๋ฆฌ ์ ํจ๊ป ์ ๊ฑฐ๋๋ค.
๐ฉ ํ๋กํ ํ์ ์ ์กฐํํ ๋ ์คํ๋๋ค๊ณ ํ๋๋ฐ, ๊ทธ๋ฌ๋ฉด ๊ฐ์ฅ ์ฒซ ์ค์ new Annotation~(PrototypeBean.class)๋ ๋ญ ํ๋ ๊ฑธ๊น?
์ฑ๊ธํค์ด๋ , ํ๋กํ ํ์ ์ด๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์์ฑํ ๋ ๊ฐ์ฅ ์ฒซ ์ค์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธด ์ค์ ํด๋์ค ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์์ฑํ๋ค. ์ด๋, ํด๋น ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๋น ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋๋ฐ, (์ฌ๊ธฐ์ init ๊ฐ์ ์ ๋ณด๊ฐ ๋ค์ด๊ฐ๋ค๊ณ ์๊ณ ์๋ค) ํ๋กํ ํ์ ์ ๊ฒฝ์ฐ ๋ฉํ๋ฐ์ดํฐ๊น์ง๋ง ์์ฑ๋๊ณ ์ค์ ๋น ์์ฑ๊น์ง๋ ์งํ๋์ง ์๋๋ค. ์์ฑ์ ์กฐํํ์ ๋!
| ํ๋กํ ํ์ ๋น๊ณผ ์ฑ๊ธํค ๋น์ ํจ๊ป ์ฌ์ฉํ๋ค๋ฉด?
โ ๋จ์ ํ๋กํ ํ์ ์ฌ์ฉ ์ ์์ฒญ ๊ณผ์
- ํ๋กํ ํ์ ๋น์ count๋ผ๋ ๋น๊ณผ, count ๊ฐ์ ์ฆ๊ฐ์ํค๋ addCount()๊ฐ ์๋ค๊ณ ๊ฐ์ ํ์.
- ํด๋ผ์ด์ธํธ๊ฐ ์คํ๋ง ์ปจํ ์ด๋์ ํ๋กํ ํ์ ๋น์ ์์ฒญํ๋ค.
- ์ปจํ ์ด๋๋ ์๋กญ๊ฒ ๋น์ ์์ฑํด์ ๋ฐํํ๋ค. ์ด๋, ๋น์ count ํ๋ ๊ฐ์ 0์ด๋ค.
- ์กฐํํ ํ๋กํ ํ์ ๋น(A)์ addCount()๋ฅผ ํธ์ถํ์ฌ count ํ๋ ๊ฐ์ ์ฆ๊ฐ์ํจ๋ค. 0->1
- ๊ฒฐ๊ณผ์ ์ผ๋ก A์ count ๊ฐ์ 1์ด ๋๋ค.
- ์ดํ, ๋ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ๊ฐ ์ปจํ ์ด๋์ ํ๋กํ ํ์ ๋น์ ์์ฒญํ๋ค.
- ์ปจํ ์ด๋๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์๋กญ๊ฒ ๋น์ ์์ฑํด์ ๋ฐํํ๋ค. count ํ๋ ๊ฐ์ 0์ด๋ค.
- ์กฐํํ ํ๋กํ ํ์ ๋น(B)์ addCount()๋ฅผ ํธ์ถํ์ฌ count ํ๋ ๊ฐ์ ์ฆ๊ฐ์ํจ๋ค. 0->1
- ๊ฒฐ๊ณผ์ ์ผ๋ก B์ count ๊ฐ์ 1์ด ๋๋ค.
- ์ฌ๊ธฐ๊น์ง๋ง ๋ณด๋ฉด ๋น์ฐํ ๊ฒฐ๊ณผ์ด๋ค. ์ด๋ฒ์๋ ์ฑ๊ธํค๊ณผ ํ๋กํ ํ์ ์ ํจ๊ป ์ฌ์ฉํด๋ณด์.
[SingletonWithPrototypeTest1.java - PrototypeBean]
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init " + this);
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
- ํ๋กํ ํ์ ์ค์ฝํ๋ฅผ ๊ฐ์ง ๋น์ด๋ค. ๋ด๋ถ์ count๋ผ๋ ํ๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
[SingletonWithPrototypeTest1.java - ClientBean]
static class ClientBean {
private final PrototypeBean prototypeBean;
@Autowired
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic() {
// DI์ ์ํด ClientBean ์์ฑ ์ ํ๋ผ๋ฏธํฐ๋ก PrototypeBean ๊ฐ์ฒด๋ฅผ ์ฃผ์
๋ฐ๊ธฐ ๋๋ฌธ์ .addCount() ๊ฐ๋ฅ
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ๊ธํค ๋น์ด๋ค.
- ์ด๋, ์์์ ์ ์ธํ ํ๋กํ ํ์ ๋น์ ์์ฑ์ ์ฃผ์ ํ์๋ค. logic()์ ํตํด count ๊ฐ์ ๋๋ฆฌ๊ณ ํด๋น ๊ฐ์ ๋ฐํํ์๋ค.
๐ฉ ์ฐธ๊ณ ๋ก, ์ด๋ฐ ์์ผ๋ก ์ ์ธํ๋ฉด ๋ด๋ถ ํ๋กํ ํ์ ์ค์ฝํ ๊ฐ์ฒด๋ ์ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํด์ผ ํ๋ค. (๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฌธ์ )
[SingletonWithPrototypeTest1.java - singletonClientUsePrototype()]
@Test
void singletonClientUsePrototype() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
ClientBean clientBean1 = ac.getBean(ClientBean.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean clientBean2 = ac.getBean(ClientBean.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(2);
}
- ์ค์ ํ ์คํธ ์ฝ๋์ด๋ค. ์์์ ์ฌ์ฉํ 2๊ฐ์ ๋น์ ์ค์ ํ์ผ๋ก ๋์์ผ๋ฉฐ ๊ฐ๊ฐ์ ์กฐํํ์๋ค.
โ ์์ฒญ ๊ณผ์
1. ClientBean์ ์ฑ๊ธํค์ด๊ธฐ ๋๋ฌธ์, ์คํ๋ง ์ปจํ ์ด๋์ ์์ฑ ์์ ์ ํจ๊ป ์์ฑ๋๊ณ ์์กด๊ด๊ณ ์ฃผ์ ์ด ์งํ๋๋ค.
- ์ด๋, ClientBean์ ์ฃผ์ ์์ ์ ํ๋กํ ํ์ ๋น์ ์์ฒญํ๊ธฐ ๋๋ฌธ์ ์ปจํ ์ด๋๋ ํ๋กํ ํ์ ๋น์ ์์ฑํ์ฌ ๋ฐํํ๋ค.
- ์ด๋, ํ๋ก๋กํ์ ๋น์ count ํ๋ ๊ฐ์ 0์ด๋ค.
2. ์ดํ, ClientBean์ ์๋ช ์ฃผ๊ธฐ๊ฐ ์คํ๋ง ์ปจํ ์ด๋์ ๋์ผํ๊ธฐ ๋๋ฌธ์ ํด๋น ํ๋กํ ํ์ ๋น์ ์ฐธ์กฐ๊ฐ์ ๋ด๋ถ ํ๋์ ๋ณด๊ดํ๋ค.
3. ํด๋ผ์ด์ธํธ๊ฐ ac.getBean()์ ํตํด ์ปจํ ์ด๋์ ์์ฒญ์ ํ๋ฉด, ์์ ๋์ผํ ClientBean์ ๋ฐํํ๋ค.
4. clientBean1.logic()์ ํธ์ถํ๋ฉด, ํ๋กํ ํ์ ๋น์ addCount()๋ฅผ ํธ์ถํ์ฌ ํ๋กํ ํ์ ๋น์ count๋ฅผ 0->1๋ก ์ฆ๊ฐ์ํจ๋ค.
5. ์ดํ, ํด๋ผ์ด์ธํธ๊ฐ ac.getBean()์ ํตํด ๋ ๋ค์ ์ปจํ ์ด๋์ ์์ฒญํ๋ฉด ๋์ผํ clientBean์ ๋ฐํํ๋ค.
- ์ด๋, ๋ด๋ถ์ ์กด์ฌํ๋ ํ๋กํ ํ์ ๋น์ ๊ณผ๊ฑฐ์ ์ฃผ์ ์ด ๋๋ฌ๋ค. ๋ ์ฌ์ฉํ๋ค๊ณ ํด์ ์๋กญ๊ฒ ์์ฑ๋๋ ๊ฒ์ด ์๋๋ค.
6, clientBean2.logic()์ ํธ์ถํ๋ฉด, addCount๋ก ์ธํด ํ๋กํ ํ์ ๋น์ count๋ฅผ 1->2๋ก ์ฆ๊ฐ์ํจ๋ค.
์ฆ, ์ ๊ณผ์ ์์ ๋ฌธ์ ๋ ์ฌ์ฉํ ๋๋ง๋ค ์๋ก ์์ฑํ๋ ค๊ณ ํ๋กํ ํ์ ์ ์ฌ์ฉํ๋ ๊ฑด๋ฐ, ์ฑ๊ธํค ๋น๊ณผ ํจ๊ป ๊ณ์ ์ ์ง๋๋ ๊ฒ์ด๋ค.
- ์ด๋, ์ฑ๊ธํค ๋น์ ์์ฑ์ ์ฃผ์ ์์ ์ ์์กด๊ด๊ณ ์ฃผ์ ์ ๋ฐ์ผ๋ฉด์ ํ๋กํ ํ์ ์ด ์๋ก ์๊ธฐ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์, ์ฃผ์ ์ด ์ข ๋ฃ๋ ์ดํ์๋ ๊ณ์ ์ ์ง๋๋ ๊ฒ.
| ํ๋กํ ํ์ ๋น๊ณผ ์ฑ๊ธํค ๋น์ ํผ์ฉ - Provider ์ด์ฉํ๊ธฐ
- ์ฌ์ฉํ ๋๋ง๋ค ์๋ก์ด ํ๋กํ ํ์ ๋น์ ์์ฑํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
1. ์ฑ๊ธํค ๋น์ด ํ๋กํ ํ์ ์ ์ฌ์ฉํ ๋๋ง๋ค ์คํ๋ง ์ปจํ ์ด๋์ ์์ฒญํ๊ธฐ.
[SingletonWithPrototypeTest1.java - ClientBean ์์ ]
static class ClientBean {
@Autowired
private ApplicationContext ac;
public int logic() {
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
- ac.getBean()์ ์ด์ฉํ๋ฉด ์กฐํ ์ ์์ฑ + ์์กด๊ด๊ณ ์ฃผ์ + ์ด๊ธฐํ ๋ฉ์๋๊น์ง ๋์ํ๋๊น ํญ์ ์๋ก์ด ํ๋กํ ํ์ ๋น์ด ๋ฐํ๋๋ค.
- ์ด๋, ์์กด๊ด๊ณ ์ฃผ์ ์ ์งํํ ๊ฒ์ด ์๋ ๋ด๋ถ์์ ์์กด๊ด๊ณ๋ฅผ ์ฐพ์ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ DL (Dependency Lookup)์ด๋ผ๊ณ ํ๋ค.
- ๊ทธ๋ฌ๋, ์ด ์ฝ๋๋ ์ปจํ ์ด๋์ ์ข ์์ ์ด๊ณ ๋จ์ ํ ์คํธ๊ฐ ์ด๋ ต๋ค. ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์!
[SingletonWithPrototypeTest1.java - ClientBean ์์ ]
static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
- ObjectProvider๋ฅผ ์ฌ์ฉํ์! (ํน์ ObjectFactory๋ฅผ ์ฌ์ฉํด๋ ๋๋ค! ๊ทผ๋ฐ Provider๋ ์กฐ๊ธ ๋ ๊ธฐ๋ฅ์ด ๋ง๋ค๊ณ ํ๋ค.)
- getObject()๋ฅผ ํธ์ถํ๋ฉด ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ํตํด ํด๋น ๋น์ ์ฐพ์์ ๋ฐํํด์ค๋ค. (DL ๊ธฐ๋ฅ)
- DL ๊ธฐ๋ฅ์ ์ ๊ณตํด์ฃผ๋, ์คํ๋ง ์ปจํ ์ด๋์ ๋์ ์กฐํํด์ฃผ๋ ๊ธฐ๋ฅ... ์ ๋๋ผ๊ณ ์๊ฐํด์ฃผ๋ฉด ๋๋ค.
๐ฉ ํ๋กํ ํ์ ๋น์ ์์ฑํ๋ ๊ฒ์ ๋น ํฉํ ๋ฆฌ์์, ์คํ๋ง ์ปจํ ์ด๋์์ ์์ฑ๋ ๋น์ ์ฐพ์ ๋ฐํํ๋ DL ๊ธฐ๋ฅ์ Provider๊ฐ ์งํ.
.getObject()๋ฅผ ํ๊ฒ ๋๋ฉด, ๋น์ด ์์ฑ๋๊ณ , ์ดํ Provider๊ฐ ์กฐํํ๋ ๊ธฐ๋ฅ์ ๋ด๋นํ๋ค.
static class ClientBean {
@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
- ์๋๋ฉด, ์๋ฐ ํ์ค์์ ์ ๊ณตํ๋ Provider๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. (javax.inject.Provider)
-> build.grade์ javax.inject:javax.inject:1๋ฅผ import ํด์ค์ผ ํจ!
- ์๋ฐ ํ์ค์ด๊ธฐ ๋๋ฌธ์ ์คํ๋ง์ ์์กดํ์ง ์๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก DL ์ ๋์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- Provider๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ํ์ ์ผ๋ก ๋น์ ์กฐํํ๋ค๋ ํน์ง์ด ์๋ค. ์ด๋ฆ ์กฐํ ๋ฑ์ ์ฌ์ฉํ๋ ค๋ฉด ApplicationContext ์ฌ์ฉํ๊ธฐ.
๐ฉ ํ๋กํ ํ์ ๋น์ด ๊ตณ์ด ํ์ํ ๊น? ๋ผ๋ ์๋ฌธ
- ์์ ์๋ฐ์ฝ๋๋ก ๊ณ์ new ์์ฑํ๋ฉด ๋๋ ๊ฒ์ด ์๋๊น? ์ถ์ ์๋ ์๋ค.
- ๋ค๋ง, ํ๋กํ ํ์ ๋น์ ์ฌ์ฉํ๋ฉด @Autowired๋ฅผ ์ง์๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด ์์ฑ ๋ฐ DI๊ฐ ํ๋ ์์ํฌ๋ฅผ ํตํด ์๋์ผ๋ก ์งํํ ์ ์๋ค. ๋ฌผ๋ก , ์๋ฐ ์ฝ๋๋ก๋ ๊ฐ๋ฅํ์ง๋ง ์ ์ด๋ฅผ ํ๋ ์์ํฌ์ ๋๊ธด๋ค๋ ๊ฑด ๊ฐ๋ฐ์๋ก์ ๋งค์ฐ ์ข์ ์ ์ด๋๊น.
| ์น ์ค์ฝํ
- ์น ํ๊ฒฝ์์ ๋์ํ๋ ์ค์ฝํ. ์คํ๋ง์ด ํด๋น ์ค์ฝํ์ ์ข ๋ฃ ์์ ๊น์ง ๊ด๋ฆฌํ๋ค.
- ๋ํ์ ์ผ๋ก request ์ค์ฝํ์ ๋ํด์ ์์๋ณด์.
โ Request Scope
- HTTP ์์ฒญ ํ๋๊ฐ ๋ค์ด์ค๊ณ ๋๊ฐ ๋๊น์ง ์ ์ง๋๋ ์ค์ฝํ์ด๋ค. ๊ฐ HTTP ์์ฒญ๋ง๋ค ๋ณ๋์ ๋น ์ธ์คํด์ค๊ฐ ์์ฑ๋๊ณ ๊ด๋ฆฌ๋๋ค.
- ๋ด๋ถ์ HTTP ์์ฒญ์ sessionId ๋ฑ์ผ๋ก (๋ณ๋์ ์๋ณ์ ์ด์ฉ) ๋์ผํ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ธ์ง ์๋ฒ ๋ด์์ ํ๋จํ๋ค.
์์ ๋ฅผ ์ํด build.gradle์ ๋ค์์ ์ถ๊ฐํ์.
implementation 'org.springframework.boot:spring-boot-starter-web'
: ๋ด์ฅ ํฐ์บฃ ์๋ฒ๋ฅผ ํ์ฉํด์ ์น ์๋ฒ์ ์คํ๋ง์ ํจ๊ป ์คํํ๊ฒ ๋๋ค.
: AnnotationConfigApplicationContext -> AnnotationConfigServletWebServerApplicationContext ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ค.
- ์์ฒญ๋ง๋ค ๋น ์์ฑ๊ณผ ์ข ๋ฃ๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด์ ๋ก๊ฑฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ฃผ์.
[MyLogger.java]
@Component
@Scope(value = "request") // request scope
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message) {
System.out.println("[" + uuid + "]" + "[" + requestURL + "] " +
message);
}
@PostConstruct
public void init() {
// ๋น ์์ฑ ์์ ์ UUID๋ฅผ ์์ฑํ์ฌ HTTP ์์ฒญ์ ๊ตฌ๋ถํด์ฃผ๊ธฐ
uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "] request scope bean create:" + this);
}
@PreDestroy
public void close() {
// ๋น ์๋ฉธ ์์ ์ ๋ฉ์์ง ๋จ๊ธฐ๊ธฐ
System.out.println("[" + uuid + "] request scope bean close:" + this);
}
}
- ์ด๋ฌ๋ฉด ๋ก๊ทธ์ [UUID][requestURL] message ํํ๋ก ์ฐํ๊ฒ ๋๋ค.
๐ฉ request scope์ ๊ฒฝ์ฐ, "์น ์์ฒญ์ด ๋ค์ด์์ผ" ๋น์ด ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๋จ์ํ ์์กด๊ด๊ณ ์ฃผ์ ์ ํ๋ ค๊ณ ํ๋ฉด
์คํ๋ง ์ปจํ ์ด๋์์ ์ฐพ์์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ๋ ์๋ฐ์ ์๋ค. ์ด๋ด ๋ ObjectProvider๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค!
[LogDemoInterceptor.java]
@RequiredArgsConstructor
public class LogDemoInterceptor implements HandlerInterceptor {
private final ObjectProvider<MyLogger> provider;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MyLogger myLogger = provider.getObject();
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
return true;
}
}
- ๊ฐ๋จํ ์ธํฐ์ ํฐ๋ฅผ ๊ตฌํํ์๋ค. HandlerInterceptor ์ธํฐํ์ด์ค์์ preHandler ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ค.
- ์ด๋ฌ๋ฉด ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ์ด ๊ฐ๊ธฐ ์ ์ ์ฌ์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค!
[WebMvcConfig.java]
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final ObjectProvider<MyLogger> provider;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogDemoInterceptor(provider))
.addPathPatterns("/log-demo");
}
}
- WebMvcConfigurer๋ฅผ ๊ตฌํํ์ฌ ๋ง๋ค์ด์ค ์ธํฐ์ ํฐ๋ฅผ ์ถ๊ฐํ๋ค.
- ObjectProvider์ ๊ฒฝ์ฐ ์คํ๋ง์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์ฃผ์ ํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ๋น ๋ฑ๋ก์ ์ฒ๋ฆฌํด์ค ํ์๋ ์๋ค.
[LogDemoService.java]
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> provider;
public void logic(String id) {
MyLogger myLogger = provider.getObject();
myLogger.log("service id = " + id);
}
}
- ๊ฐ๋จํ ์๋น์ค ๊ณ์ธต์ ๊ตฌํํด์ฃผ์๋ค. @Service ๋ด์ @Component๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋น ๋ฑ๋ก์ ์ ๋๋ค.
- ์๋น์ค ๊ณ์ธต์ ๊ฒฝ์ฐ ์น๊ณผ ๊ด๋ จ๋๋ ์ฝ๋๊ฐ ์๋ ๊ฒ์ด ์ข๋ค. (HttpServletRequest ๊ฐ์)
[LogDemoController.java]
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
@RequestMapping("/log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
logDemoService.logic("testId");
return "OK";
}
}
- ์ปจํธ๋กค๋ฌ ๊ณ์ธต์ด๋ค. /log-demo๋ก ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ฃผ์์ผ๋ฉฐ (์ธํฐ์ ํฐ ๋ฑ๋ก ์ ๊ฒฝ๋ก์ ์ผ์นํ๊ฒ) ๋จ์ํ ์๋น์ค๋ฅผ ํธ์ถํ๋ ์์ผ๋ก ๋ณ๊ฒฝํ์๋ค.
์ดํ, ๋ธ๋ผ์ฐ์ ์์ http://localhost:8080/log-demo๋ฅผ ์คํํด๋ณด๋ฉด...!
๋ค์๊ณผ ๊ฐ์ด ์์ฒญ๋ง๋ค ๊ฒฐ๊ณผ๊ฐ ๋งค์ฐ ์ ๋์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค :D
- ObjectProvider ๋๋ถ์ .getObject()๋ฅผ ํธ์ถํ๋ ์์ ๊น์ง Request Scope ๋น์ ์์ฑ์ ์ง์ฐ์ํฌ ์ ์์๋ค.
- .getObject()๋ฅผ ํธ์ถํ๋ ์์ ์๋ ์ด๋ฏธ HTTP ์์ฒญ์ด ์งํ๋๊ณ ์๊ธฐ ๋๋ฌธ์ request scope ๋น์ ์์ฑ์ด ์ ์ ์ฒ๋ฆฌ๊ฐ ๋๋ค.
- ์ฆ, ๊ฒฐ๊ณผ์ ์ผ๋ก ์์ฒญ์ด ๋ค์ด์์ ๋ .getObject๋ฅผ ํตํด์ MyLogger๊ฐ ์์ฑ๋๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
๐ฉ ์ ํํ๊ฒ๋, ์์ฒญ์ด ๋ค์ด์์ Request Scope์ด ํ์ํ ์์ ์ ObjectProvider๋ฅผ ํตํด Request Scope ๋น์ ์ฐพ์์์ (DL)
๋ง์ฝ ์์ผ๋ฉด ํด๋น ๋น์, ์์ผ๋ฉด ์๋ก ์์ฑํด์ ๋ฆฌํดํด์ค๋ค๊ณ ๋ณด๋ฉด ๋๋ค!
| ์ค์ฝํ์ ํ๋ก์ ์ ์ฉํ๊ธฐ
[MyLogger.java]
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
private String uuid;
private String requestURL;
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public void log(String message) {
System.out.println("[" + uuid + "]" + "[" + requestURL + "] " +
message);
}
@PostConstruct
public void init() {
uuid = UUID.randomUUID().toString();
System.out.println("[" + uuid + "] request scope bean create:" + this);
}
@PreDestroy
public void close() {
System.out.println("[" + uuid + "] request scope bean close:" + this);
}
}
- proxyMode๊ฐ ์ถ๊ฐ๋์๋ค. ์ ์ฉ ๋์์ด ํด๋์ค๋ฉด TARGET_CLASS๋ฅผ, ์ธํฐํ์ด์ค๋ INTERFACES๋ฅผ ์ ํํ๋ค.
- ์ด๋ฌ๋ฉด MyLogger์ ๊ฐ์ง ํ๋ก์ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ , HTTP Request์ ์๊ด์์ด ๊ฐ์ง ํ๋ก์ ํด๋์ค๋ฅผ ๋ฏธ๋ฆฌ ๋น์ ์ฃผ์ ํด๋ ์ ์๋ค.
- ์ฝ๋๋ ์์ ํด์ฃผ์.
[LogDemoInterceptor.java]
@RequiredArgsConstructor
public class LogDemoInterceptor implements HandlerInterceptor {
private final MyLogger myLogger;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
return true;
}
}
[LogDemoService.java]
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
- ์คํํด๋ณด๋ฉด ์๊น์ ๊ฐ์ด ์ ๋์ํ๋ค.
- ๊ทธ๋ ๋ค๋ฉด, ์คํ๋ง ์์ ์ myLogger๋ ๋ญ๊ฐ ์ฃผ์ ๋ ๊ฑธ๊น...?
- ๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณด๋ฉด, CGLIB๋ก ์ธํ ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ฃผ์ ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
CGLIB๋ฅผ ํตํด MyLogger๋ฅผ ์์๋ฐ์ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
-> ์คํ๋ง ์ปจํ ์ด๋์ "myLogger"๋ผ๋ ์ด๋ฆ์ผ๋ก ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ฑ๋กํ๊ฒ ๋๋ค. DI ์์๋ ์ด ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๊ฐ ๋ค์ด๊ฐ๋ค!
๐ฉ ์ค์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด, ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ ๋ด๋ถ์์ ์ง์ง ๋น์ ์์ฒญํ๋ ์์ ๋ก์ง์ ํธ์ถํ๋ฉฐ,
ํด๋ผ์ด์ธํธ๊ฐ myLogger.logic() ํธ์ถ -> ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ค์ ๊ฐ์ฒด์ myLogger.logic() ํธ์ถ -> ์คํ์ผ๋ก ๋์ํ๋ค.
- ์ด๋, ์ค์ request scope์ ๊ด๊ณ์์ด ๊ทธ๋ฅ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด์ด๋ฉฐ, ์ฑ๊ธํค์ฒ๋ผ ๋์ํ๊ฒ ๋๋ค. (๊ทธ๋ฌ๋, ์ค์ ์ฑ๊ธํค์ ์๋๋ผ๋ ๊ฑฐ!)
- ํ๋ก์ ๋ชจ๋๋ฅผ ์ฌ์ฉํ๋ฉด CGLIB๋ฅผ ํ์ฉํ ๋น ๊ฐ์ฒด๋ ์คํ๋ง ์ปจํ ์ด๋ ์์ฑ ์ ๋ฑ ํ ๋ฒ๋ง ํธ์ถ๋๋ค. ๊ทธ๋ฌ๋, ํ๋ก์ ๊ฐ์ฒด๊ฐ ํธ์ถ๋ ๋๋ง๋ค ์ง์ง ๊ฐ์ฒด๋ฅผ ๋งค๋ฒ ์์ฑํ ์ง ๋ง์ง๋ ์ง์ง ๋น์ ์ค์ฝํ์ ๋ฐ๋ผ์ ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์, ์ฑ๊ธํค์ฒ๋ผ ๋์ํ๊ฒ ๋๋ฉด ์ฑ๊ธํค์ด ์๋๋ฐ๋ ๊ทธ๋ ๊ฒ ๋์ํ๋ ๊ฑฐ๋๊น ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค. ์ฌ์ฉ ์ ์ฃผ์ํ ๊ฒ.
โญ ์ค์ ๊ฐ์ฒด ์กฐํ๋ฅผ ํ์ํ ์์ ๊น์ง ์ง์ฐ ์ฒ๋ฆฌํ๋ค๋ ๊ฒ ํต์ฌ์ด๋ฉฐ,
์ ๋ ธํ ์ด์ ์ค์ ๋ณ๊ฒฝ๋ง์ผ๋ก ์๋ณธ ๊ฐ์ฒด๋ฅผ ํ๋ก์ ๊ฐ์ฒด๋ก ๋์ฒดํ ์ ์๋ค๋ ๊ฒ ๋คํ์ฑ, DI ์ปจํ ์ด๋์ ๊ฐ์ ์ด๋ค.
์ด๋ ๊ฒ ์คํ๋ง ํต์ฌ์๋ฆฌ๋ฅผ ์๊ฐํ์๋ค! ๋ ๋ฒ์งธ ๋ฃ๋ ๊ฑด๋ฐ๋ ๊ต์ฅํ ์๋กญ๊ณ ...
์์ง ๋ชจ๋ฅด๋ ๊ฒ์ด ํฌ์ฑ์ด๋ผ๋ ์๊ฐ์ด ๋ค์๋ค. ๋ ์ด์ฌํ ๋ค์ด์ผ๊ฒ ๋ค... ๐ฅ๐ฆ