[์คํ๋ง ๊ธฐ๋ณธ] 05. ์ฑ๊ธํค ์ปจํ ์ด๋
by Hi.Claire๐ฅ๏ธ ์คํ๋ง ํต์ฌ ์๋ฆฌ - ๊ธฐ๋ณธํธ (๊น์ํ, ์ธํ๋ฐ)
์น์ 5. ์ฑ๊ธํค ์ปจํ ์ด๋
05-1. ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ฑ๊ธํค
๋๋ถ๋ถ์ ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ค.
(๋ฌผ๋ก ๋ฐฐ์น ์ ํ๋ฆฌ์ผ์ด์ , ๋ฐ๋ชฌ ์ ํ๋ฆฌ์ผ์ด์ ๋ฑ ์น์ด ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ๋ ์ผ๋ง๋ ์ง ๊ฐ๋ฅํ๋ค.)
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณดํต ์ฌ๋ฌ ๊ณ ๊ฐ์ด ๋์์ ์์ฒญ์ ํ๋ค.
์ด๋ ์คํ๋ง ์๋ ์์ํ DI ์ปจํ ์ด๋(์ : AppConfig)๋ ๊ณ ๊ฐ์ด ์์ฒญ์ ํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์๋ก ์์ฑํ๋ฏ๋ก ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ์ฌํ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ํด๋น ๊ฐ์ฒด๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๊ณ ๊ณต์ ๋๋๋ก ์ค๊ณํ๋ฉด ๋๋ค.
-> ์ฑ๊ธํค ํจํด
์คํ๋ง ์๋ ์์ํ DI ์ปจํ ์ด๋
test/java/hello/core/singleton/SingletonTest.java
package hello.core.singleton;
import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class SingletonTest {
@Test
@DisplayName("์คํ๋ง์๋ ์์ํ DI ์ปจํ
์ด๋")
public void pureContainer() {
AppConfig appConfig = new AppConfig();
// 1. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService1 = appConfig.memberService();
// 2. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService2 = appConfig.memberService();
// ์ฐธ์กฐ๊ฐ์ด ๋ค๋ฅธ ๊ฒ์ ํ์ธ
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
// memberService1 != memberService2
assertThat(memberService1).isNotSameAs(memberService2);
}
}
ํ ์คํธ ๊ฒฐ๊ณผ
memberService1๊ณผ memberService2์ ์ฐธ์กฐ๊ฐ์ด ์๋ก ๋ค๋ฅธ ๊ฒ์ ์ ์ ์๋ค.
05-2. ์ฑ๊ธํค ํจํด
์ฑ๊ธํค ํจํด
์ฑ๊ธํค ํจํด์ ํด๋์ค์ ์ธ์คํด์ค๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๋ ๊ฒ์ ๋ณด์ฅํ๋ ๋์์ธ ํจํด์ด๋ค.
๋ฐ๋ผ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ 2๊ฐ ์ด์ ์์ฑํ์ง ๋ชปํ๋๋ก ๋ง์๋์ด์ผ ํ๋ค.
์ฑ๊ธํค ํจํด์ ์ ์ฉํ๊ธฐ
1. static ์์ญ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฏธ๋ฆฌ ํ๋ ์์ฑํด์ ์ฌ๋ ค๋๋ค.
2. ์ด ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ํ์ํ๋ฉด ์ค์ง getInstance() ๋ฉ์๋๋ฅผ ํตํด์๋ง ์กฐํํ ์ ์๋ค. ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํญ์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค.
3. private ์์ฑ์๋ฅผ ์ฌ์ฉํด์ ์ธ๋ถ์์ ์์๋ก new ํค์๋๋ก ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋ง๋๋ค.
(์ฐธ๊ณ ) static๊ณผ ์ฑ๊ธํค์ ๋ํ ์ ๋ฆฌ
https://moominie.tistory.com/14
06-5. ์ธ์คํด์ค ๋ฉค๋ฒ์ ์ ์ ๋ฉค๋ฒ
06. ํด๋์ค 06-5. ์ธ์คํด์ค ๋ฉค๋ฒ์ ์ ์ ๋ฉค๋ฒ ์ธ์คํด์ค(instance) ๋ฉค๋ฒ ๊ฐ์ฒด๋ง๋ค ๊ฐ์ง๊ณ ์๋ ๋ฉค๋ฒ ๊ฐ์ฒด(์ธ์คํด์ค)๋ฅผ ์์ฑํ ํ ์ฌ์ฉํ ์ ์๋ ํ๋์ ๋ฉ์๋ ์ธ์คํด์ค ํ๋ : ํ ์์ญ์ ๊ฐ์ฒด๋ง๋ค ๊ฐ
moominie.tistory.com
test/java/hello/core/singleton/SingletonService.java
package hello.core.singleton;
public class SingletonService {
// 1. static ์์ญ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฏธ๋ฆฌ ํ๋ ์์ฑํด์ ์ฌ๋ ค๋๋ค.
private static final SingletonService instance = new SingletonService();
// 2. ์ด ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ํ์ํ๋ฉด ์ค์ง getInstance() ๋ฉ์๋๋ฅผ ํตํด์๋ง ์กฐํํ ์ ์๋ค. ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํญ์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค.
public static SingletonService getInstance() {
return instance;
}
// 3. private ์์ฑ์๋ฅผ ์ฌ์ฉํด์ ์ธ๋ถ์์ ์์๋ก new ํค์๋๋ก ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋ง๋๋ค.
private SingletonService() {
}
public void logic() {
System.out.println("์ฑ๊ธํค ๊ฐ์ฒด ๋ก์ง ํธ์ถ");
}
}
test/java/hello/core/singleton/SingletonTest.java
package hello.core.singleton;
import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
public class SingletonTest {
@Test
@DisplayName("์คํ๋ง์๋ ์์ํ DI ์ปจํ
์ด๋")
public void pureContainer() {
AppConfig appConfig = new AppConfig();
// 1. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService1 = appConfig.memberService();
// 2. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑ
MemberService memberService2 = appConfig.memberService();
// ์ฐธ์กฐ๊ฐ์ด ๋ค๋ฅธ ๊ฒ์ ํ์ธ
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
// memberService1 != memberService2
assertThat(memberService1).isNotSameAs(memberService2);
}
@Test
@DisplayName("์ฑ๊ธํค ํจํด์ ์ ์ฉํ ๊ฐ์ฒด ์ฌ์ฉ")
public void SingletonServiceTest() {
// new ์ฐ์ฐ์๋ก ๊ฐ์ฒด ์์ฑ ๋ถ๊ฐ
// SingletonService singletonService = new SingletonService();
// 1. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฐํ
SingletonService singletonService1 = SingletonService.getInstance();
// 2. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฐํ
SingletonService singletonService2 = SingletonService.getInstance();
// ์ฐธ์กฐ๊ฐ์ด ๊ฐ์ ๊ฒ์ ํ์ธ
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
// singletonService1 == singletonService2
assertThat(singletonService1).isSameAs(singletonService2);
}
}
ํ ์คํธ ๊ฒฐ๊ณผ
singletonService1๊ณผ singletonService2์ ์ฐธ์กฐ๊ฐ์ด ๊ฐ์ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋ ๊ฒ ์ฑ๊ธํค ํจํด์ ์ ์ฉํ๋ฉด ๊ณ ๊ฐ์ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์๋ก ์์ฑํ์ง ์๊ณ , ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๊ณต์ ํด์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
ํ์ง๋ง ์ฑ๊ธํค ํจํด์ ๋ค์๊ณผ ๊ฐ์ ์ ๋ง์ ๋ฌธ์ ์ ๋ค์ ๊ฐ์ง๊ณ ์๋ค.
์ฑ๊ธํค ํจํด์ ๋ฌธ์ ์
- ์ฑ๊ธํค ํจํด์ ๊ตฌํํ๋ ์ฝ๋ ์์ฒด๊ฐ ๋ง์ด ๋ค์ด๊ฐ๋ค.
- ์์กด๊ด๊ณ์ ํด๋ผ์ด์ธํธ๊ฐ ์ญํ ์ด ์๋ ๊ตฌ์ฒด ํด๋์ค์ ์์กดํ๋ค. (์ : MemberServiceImpl.getInstance())
-> DIP๋ฅผ ์๋ฐํ๋ค.
- ์์ ๊ฐ์ ์ด์ ๋ก OCP๋ฅผ ์๋ฐํ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
- ํ ์คํธํ๊ธฐ ์ด๋ ต๋ค.
- ๋ด๋ถ ์์ฑ์ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ด๊ธฐํํ๊ธฐ ์ด๋ ต๋ค.
- private ์์ฑ์๋ก ์์ ํด๋์ค๋ฅผ ๋ง๋ค๊ธฐ ์ด๋ ต๋ค.
- ๊ฒฐ๋ก ์ ์ผ๋ก ์ ์ฐ์ฑ์ด ๋จ์ด์ง๋ค.
- ์ํฐํจํด์ผ๋ก ๋ถ๋ฆฌ๊ธฐ๋ ํ๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ์คํ๋ง์ ์ด๋ป๊ฒ ํด๊ฒฐํ ๊น?
05-3. ์ฑ๊ธํค ์ปจํ ์ด๋
์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ํจํด์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ฉด์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌํ๋ค.
์ง๊ธ๊น์ง ์ฐ๋ฆฌ๊ฐ ํ์ตํ ์คํ๋ง ๋น์ด ๋ฐ๋ก ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌ๋๋ ๋น์ด๋ค.
์ฑ๊ธํค ์ปจํ ์ด๋
- ์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ํจํด์ ์ ์ฉํ์ง ์์๋ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌํ๋ค.
- ์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ์ปจํ ์ด๋ ์ญํ ์ ํ๋ค. ์ด๋ ๊ฒ ์ฑ๊ธํค ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ๋ผ ํ๋ค.
- ์คํ๋ง ์ปจํ ์ด๋์ ์ด๋ฐ ๊ธฐ๋ฅ ๋๋ถ์ ์ฑ๊ธํค ํจํด์ ๋ชจ๋ ๋จ์ ์ ํด๊ฒฐํ๋ฉด์ ๊ฐ์ฒด๋ฅผ ์ฑ๊ธํค์ผ๋ก ์ ์งํ ์ ์๋ค.
-> ์ฑ๊ธํค ํจํด์ ์ํ ์ง์ ๋ถํ ์ฝ๋๊ฐ ๋ค์ด๊ฐ์ง ์์๋ ๋๋ค.
-> DIP, OCP, ํ ์คํธ, private ์์ฑ์๋ก๋ถํฐ ์์ ๋กญ๊ฒ ์ฑ๊ธํค์ ์ฌ์ฉํ ์ ์๋ค.
์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ๋ ํ ์คํธ ์ฝ๋
test/java/hello/core/singleton/SingletonTest.java
(์๋ต)
@Test
@DisplayName("์คํ๋ง ์ปจํ
์ด๋์ ์ฑ๊ธํค")
public void springContainer() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
// 1. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฐํ
MemberService memberService1 = ac.getBean("memberService", MemberService.class);
// 2. ์กฐํ : ํธ์ถํ ๋๋ง๋ค ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฐํ
MemberService memberService2 = ac.getBean("memberService", MemberService.class);
// ์ฐธ์กฐ๊ฐ์ด ๊ฐ์ ๊ฒ์ ํ์ธ
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
// memberService1 == memberService2
assertThat(memberService1).isSameAs(memberService2);
}
(์๋ต)
ํ ์คํธ ๊ฒฐ๊ณผ
memberService1๊ณผ memberService2๋ ์ฐธ์กฐ๊ฐ์ด ๊ฐ๋ค.
์ฆ, ์คํ๋ง ์ปจํ ์ด๋๋ ์คํ๋ง ๋น์ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌํ๋ค.
์ฑ๊ธํค ์ปจํ ์ด๋
์คํ๋ง ์ปจํ ์ด๋๋ ๊ณ ๊ฐ์ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๊ณต์ ํด์ ํจ์จ์ ์ผ๋ก ์ฌ์ฌ์ฉํ๋ค.
(์ฐธ๊ณ ) ์คํ๋ง์ ๊ธฐ๋ณธ ๋น ๋ฑ๋ก ๋ฐฉ์์ ์ฑ๊ธํค์ด์ง๋ง ์ฑ๊ธํค ๋ฐฉ์๋ง ์ง์ํ๋ ๊ฒ์ ์๋๋ค. ์์ฒญํ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํด์ ๋ฐํํ๋ ๊ธฐ๋ฅ๋ ์ ๊ณตํ๋ค. ์์ธํ ๋ด์ฉ์ ๋ค์ ๋น ์ค์ฝํ์์ ์์๋ณด์.
05-4. ์ฑ๊ธํค ๋ฐฉ์์ ์ฃผ์์
- ๋ฌด์ํ(stateless)๋ก ์ค๊ณํด์ผ ํ๋ค.
-> ์ฑ๊ธํค ๋ฐฉ์์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ํ๋๋ง ์์ฑํด์ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ ์ฑ๊ธํค ๊ฐ์ฒด๋ ์ํ๋ฅผ ์ ์ง(stateful)ํ๊ฒ ์ค๊ณํ๋ฉด ์๋๋ค.
- ํน์ ํด๋ผ์ด์ธํธ์ ์์กด์ ์ธ ํ๋๊ฐ ์์ผ๋ฉด ์๋๋ค.
- ํน์ ํด๋ผ์ด์ธํธ๊ฐ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ ํ๋๊ฐ ์์ผ๋ฉด ์๋๋ค.
- ๊ฐ๊ธ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํด์ผ ํ๋ค.
- ํ๋ ๋์ ์ ์๋ฐ์์ ๊ณต์ ๋์ง ์๋ ์ง์ญ๋ณ์, ํ๋ผ๋ฏธํฐ, ThreadLocal ๋ฑ์ ์ฌ์ฉํด์ผ ํ๋ค.
- ์คํ๋ง ๋น์ ํ๋์ ๊ณต์ ๊ฐ์ ์ค์ ํ๋ฉด ํฐ ์ฅ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
์ํ๋ฅผ ์ ์งํ ๊ฒฝ์ฐ ๋ฐ์ํ๋ ๋ฌธ์ ์ ์์
test/java/hello/core/singleton/StatefulService.java
package hello.core.singleton;
public class StatefulService {
private int price; // ์ํ๋ฅผ ์ ์งํ๋ ํ๋
public void order(String name, int price) {
System.out.println("name = " + name + ", price = " + price);
this.price = price; // ์ฌ๊ธฐ๊ฐ ๋ฌธ์ !
}
public int getPrice() {
return price;
}
}
test/java/hello/core/singleton/StatefulServiceTest.java
package hello.core.singleton;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
class StatefulServiceTest {
@Test
void StatefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);
// TreadA : A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ
statefulService1.order("userA", 10000);
// TreadB : B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ
statefulService2.order("userB", 20000);
// A์ฌ์ฉ์์ ์ฃผ๋ฌธ๊ธ์ก์ ๊ธฐ๋์ ๋ค๋ฅด๊ฒ 10000์์ด ์๋ 20000์์ด ์ถ๋ ฅ๋๋ค.
System.out.println("userA์ ์ฃผ๋ฌธ ๊ธ์ก : " + statefulService1.getPrice());
assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
public static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
ํ ์คํธ ๊ฒฐ๊ณผ
์์ ํ ์คํธ ์์ ์์ ์ต๋ํ ๋จ์ํ๊ฒ ์ค๋ช ํ๊ธฐ ์ํด ์ค์ ์ฐ๋ ๋๋ ์ฌ์ฉํ์ง ์์๋ค.
ThreadA๊ฐ ์ฌ์ฉ์A ์ฝ๋๋ฅผ ํธ์ถํ๊ณ , ThreadB๊ฐ ์ฌ์ฉ์B ์ฝ๋๋ฅผ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํ์.
StatefulService์ price ํ๋๋ ๊ณต์ ๋๋ ํ๋์ธ๋ฐ ํน์ ํด๋ผ์ด์ธํธ์ ์ํด ๊ฐ์ด ๋ณ๊ฒฝ๋์๋ค.
๊ทธ ๊ฒฐ๊ณผ ์ฌ์ฉ์A์ ์ฃผ๋ฌธ๊ธ์ก์ 10000์์ด ์๋ 20000์์ด๋ผ๋ ๊ฒฐ๊ณผ๊ฐ ๋์๋ค.
์ค๋ฌด์์ ์ด๋ด ๊ฒฝ์ฐ ํด๊ฒฐํ๊ธฐ ์ด๋ ค์ด ํฐ ๋ฌธ์ ๋ค์ด ํฐ์ง๊ธฐ ๋๋ฌธ์ ๊ณต์ ํ๋๋ ์กฐ์ฌํด์ผ ํ๋ค.
-> ์คํ๋ง ๋น์ ํญ์ ๋ฌด์ํ(stateless)๋ก ์ค๊ณํ์!
์ง์ญ๋ณ์๋ฅผ ์ฌ์ฉํด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์
test/java/hello/core/singleton/StatefulService.java
package hello.core.singleton;
public class StatefulService {
// private int price; // ์ํ๋ฅผ ์ ์งํ๋ ํ๋
public int order(String name, int price) {
System.out.println("name = " + name + ", price = " + price);
return price;
}
// public int getPrice() {
// return price;
// }
}
test/java/hello/core/singleton/StatefulServiceTest.java
package hello.core.singleton;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
class StatefulServiceTest {
@Test
void StatefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);
// TreadA : A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ
int priceA = statefulService1.order("userA", 10000);
// TreadB : B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ
int priceB = statefulService2.order("userB", 20000);
System.out.println("userA์ ์ฃผ๋ฌธ ๊ธ์ก : " + priceA);
assertThat(priceA).isEqualTo(10000);
}
public static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
ํ ์คํธ ๊ฒฐ๊ณผ
05-5. @Configuration๊ณผ ์ฑ๊ธํค
๋ค์ AppConfig๋ก ๋์๊ฐ์ ์ด์ํ ๋ถ๋ถ์ ์ฐพ์๋ณด์.
main/java/hello/core/AppConfig.java
(์๋ต)
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
(์๋ต)
}
memberService ๋น์ ๋ง๋ค ๋ memberRepository()๋ฅผ ํธ์ถํ๋ค.
-> memberRepository() ๋ฉ์๋๋ new MemoryMemberRepository()๋ฅผ ํธ์ถํ๋ค.
orderService ๋น์ ๋ง๋ค ๋์๋ memberRepository()๋ฅผ ํธ์ถํ๋ค.
-> memberRepository() ๋ฉ์๋๋ new MemoryMemberRepository()๋ฅผ ํธ์ถํ๋ค.
๋ ์์ฒด์ ์ผ๋ก memberRepository ๋น์ ๋ง๋๋ ๋ฉ์๋๋ ์๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๊ฐ๊ฐ ๋ค๋ฅธ 3๊ฐ์ MemoryMemberRepository๊ฐ ์์ฑ๋๋ฉด์ ์ฑ๊ธํค์ด ๊นจ์ง๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.
์คํ๋ง ์ปจํ ์ด๋๋ ์ด ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๊ณ ์์๊น?
๊ฒ์ฆ ์ฉ๋์ ํ ์คํธ ์ฝ๋ ์ถ๊ฐ
main/java/hello/core/member/MemberServiceImpl.java
package hello.core.member;
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
(์๋ต)
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
main/java/hello/core/order/OrderServiceImpl.java
package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
(์๋ต)
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
ํ ์คํธ
test/java/hello/core/singleton/ConfigurationSingletonTest.java
package hello.core.singleton;
import hello.core.AppConfig;
import hello.core.member.MemberRepository;
import hello.core.member.MemberServiceImpl;
import hello.core.order.OrderServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigurationSingletonTest {
@Test
public void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean(MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean(OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean(MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
// ๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ค.
System.out.println("memberService -> memberRepository1 = " + memberRepository1);
System.out.println("orderService -> memberRepository2 = " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
assertThat(memberRepository1).isSameAs(memberRepository2);
assertThat(memberRepository2).isSameAs(memberRepository);
assertThat(memberRepository).isSameAs(memberRepository1);
}
}
ํ ์คํธ ๊ฒฐ๊ณผ
ํ ์คํธ ๊ฒฐ๊ณผ ์ธ ๊ฐ์ memberRepository ๋ชจ๋ ์ฐธ์กฐ๊ฐ์ด ๊ฐ์์ ์ ์ ์๋ค.
์ฆ, memberRepository์ ์ธ์คํด์ค๋ ๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๊ฐ ๊ณต์ ๋์ด ์ฌ์ฉ๋๋ค.
AppConfig์ ํธ์ถ ๋ก๊ทธ ๋จ๊ธฐ๊ธฐ
main/java/hello/core/AppConfig.java
(์๋ต)
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
(์๋ต)
}
์คํ๋ง ์ปจํ ์ด๋๊ฐ ๊ฐ๊ฐ @Bean์ ํธ์ถํด์ ์คํ๋ง ๋น์ ์์ฑํ๋ค.
๊ทธ๋ ๋ค๋ฉด memberRepository()๋ ๋ค์๊ณผ ๊ฐ์ด ์ด 3๋ฒ์ด ํธ์ถ๋๋ค.
1. @Bean memberRepository() ํธ์ถ
2. memberService() ๋ก์ง์์ memberRepository() ํธ์ถ
3. orderService() ๋ก์ง์์ memberRepository() ํธ์ถ
๊ทธ๋ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ก๊ทธ๊ฐ ์ฐํ ๊ฒ์ผ๋ก ์์ํ ์ ์๋ค.
call AppConfig.memberRepository
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
...
์ค์ ์ถ๋ ฅ ๊ฒฐ๊ณผ
๊ทธ๋ฐ๋ฐ ์ค์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด call AppConfig.memberRepository ๋ 1๋ฒ๋ง ์ฐํ์๋ค.
05-6. @Configuration๊ณผ ๋ฐ์ดํธ์ฝ๋ ์กฐ์์ ๋ง๋ฒ
์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ์ด๋ฏ๋ก ์คํ๋ง ๋น์ด ์ฑ๊ธํค์ด ๋๋๋ก ๋ณด์ฅํด์ฃผ์ด์ผ ํ๋ค.
๊ทธ๋ ์ง๋ง ์คํ๋ง์ด ์๋ฐ ์ฝ๋๊น์ง ์ด๋ป๊ฒ ํ๊ธฐ๋ ํ๋ค๋ค.
AppConfig์ ์๋ฐ ์ฝ๋๋ก๋ง ๋ดค์ ๋์๋ memberRepository()๋ ๋ถ๋ช ์ด 3๋ฒ์ด ํธ์ถ๋๋ ๊ฒ์ด ๋ง๋ค.
๊ทธ๋์ ์คํ๋ง์ ํด๋์ค์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค.
๋ชจ๋ ๋น๋ฐ์ @Configuration์ ์ ์ฉํ AppConfig์ ์๋ค.
ํ ์คํธ์ฉ ์ฝ๋
test/java/hello/core/singleton/ConfigurationSingletonTest.java
@Test
public void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
// AppConfig๋ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋๋ค.
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean.getClass() = " + bean.getClass());
}
AnnotationConfigApplicationContext์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธด ๊ฐ์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋๋ค.
๊ทธ๋์ AppConfig๋ ์คํ๋ง ๋น์ด ๋๋ค.
AppConfig ์คํ๋ง ๋น์ ์กฐํํด์ ํด๋์ค ์ ๋ณด๋ฅผ ์ถ๋ ฅํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
bean.getClass() = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$b8933e89
์์ํ ํด๋์ค๋ผ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ถ๋ ฅ๋์ด์ผ ํ๋ค.
class hello.core.AppConfig
๊ทธ๋ฐ๋ฐ ํด๋์ค๋ช ์ xxxCGLIB๊ฐ ๋ถ์ผ๋ฉด์ ์๋นํ ๋ณต์กํด์ง ๊ฒ์ ์ ์ ์๋ค.
์ด๊ฒ์ ์คํ๋ง์ด CGLIB๋ผ๋ ๋ฐ์ดํธ์ฝ๋ ์กฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ๋ด๊ฐ ๋ง๋ AppConfig ํด๋์ค๋ฅผ ์์๋ฐ์ ์์์ ๋ค๋ฅธ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ๋ค๋ฅธ ํด๋์ค๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ ๊ฒ์ด๋ค.
๊ทธ ์์์ ํด๋์ค๊ฐ ๋ฐ๋ก ์ฑ๊ธํค์ด ๋ณด์ฅ๋๋๋ก ํด์ค๋ค.
CGLIB์ ๋ด๋ถ ์ฝ๋๋ฅผ ์์ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด๋ค.
AppConfig@CGLIB์ ์์ ์ฝ๋
@Bean
public MemberRepository memberRepository() {
if(memoryMemberRepository๊ฐ ์ด๋ฏธ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก๋์ด ์์ผ๋ฉด?) {
return ์คํ๋ง ์ปจํ
์ด๋์์ ์ฐพ์์ ๋ฐํ;
} else {
// ์คํ๋ง ์ปจํ
์ด๋์ ์์ผ๋ฉด ๊ธฐ์กด ๋ก์ง์ ํธ์ถํด์ MemoryMemberRepository๋ฅผ ์์ฑํ๊ณ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก
return ๋ฐํ;
}
}
@Bean์ด ๋ถ์ ๋ฉ์๋๋ง๋ค ์ด๋ฏธ ์คํ๋ง ๋น์ด ์กด์ฌํ๋ฉด ์กด์ฌํ๋ ๋น์ ๋ฐํํ๊ณ , ์์ผ๋ฉด ์์ฑํด์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ ํ ๋ฐํํ๋ ์ฝ๋๊ฐ ๋์ ์ผ๋ก ๋ง๋ค์ด์ง๋ค.
์ด ๋๋ถ์ ์ฑ๊ธํค์ด ๋ณด์ฅ๋๋ค.
(์ฐธ๊ณ ) AppConfig@CGLIB๋ AppConfig์ ์์ ํ์ ์ด๋ฏ๋ก AppConfig ํ์ ์ผ๋ก ์กฐํํ ์ ์๋ค.
@Configuration์ ์ ์ฉํ์ง ์๊ณ , @Bean๋ง ์ ์ฉํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
main/java/hello/core/AppConfig.java
//@Configuration
public class AppConfig {
(์๋ต)
AppConfig์ @Configuration์ ์ญ์ ํ๊ณ configurationDeep()์ ๋๊ฐ์ด ์คํํด๋ณด์.
bean.getClass() = class hello.core.AppConfig
@Configuration์ ์ญ์ ํ๋ฉด AppConfig๊ฐ CGLIB ๊ธฐ์ ์์ด ์์ํ AppConfig๋ก ์คํ๋ง ๋น์ ๋ฑ๋ก๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ MemberRepository๊ฐ ์ด 3๋ฒ ํธ์ถ๋ ๊ฒ์ ์ ์ ์๋ค.
ํ ๋ฒ์ @Bean์ ์ํด ์คํ๋ง ์ปจํ ์ด๋์ ๋ฑ๋กํ๊ธฐ ์ํด์์ด๊ณ , ๋๋จธ์ง ๋ ๋ฒ์ ๊ฐ๊ฐ memberRepository()๋ฅผ ํธ์ถํ๋ฉด์ ๋ฐ์ํ ์ฝ๋๋ค.
์ธ์คํด์ค๊ฐ ๊ฐ์์ง ํ์ธํ๊ธฐ ์ํด configurationTest()๋ฅผ ๋ค์ ์คํํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
๊ฐ๊ฐ ๋ค ๋ค๋ฅธ MemoryMemberRepository ์ธ์คํด์ค๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
์ ๋ฆฌ
- @Bean๋ง ์ฌ์ฉํด๋ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋์ง๋ง ์ฑ๊ธํค์ ๋ณด์ฅํ์ง ์๋๋ค.
-> memberRepository()์ฒ๋ผ ์์กด๊ด๊ณ ์ฃผ์ ์ด ํ์ํด์ ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํ ๋ ์ฑ๊ธํค์ ๋ณด์ฅํ์ง ์๋๋ค.
- ์คํ๋ง ์ค์ ์ ๋ณด๋ ํญ์ @Configuration์ ์ฌ์ฉํ์.
'๐ฑ Spring > ์คํ๋ง ํต์ฌ ์๋ฆฌ - ๊ธฐ๋ณธํธ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ธ๋ก๊ทธ์ ์ ๋ณด
Claire's Study Note
Hi.Claire