[Spring] 5.์ฑ๊ธํค ์ปจํ ์ด๋
1. ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ฑ๊ธํค
- AppConfig์ ๊ฒฝ์ฐ ๋ฉค๋ฒ ์๋น์ค ๋ฌ๋ผ๊ณ ํ๋ฉด ํด๋น Impl ์์ฑํด์ ๋ฐํ
- ์์ฒญํ ๋๋ง๋ค memberService ์์ฑ
์คํ๋ง ์๋ ์์ํ DI ์ปจํ ์ด๋ ํ ์คํธ
* ์์ฒญ์ด ์ฌ๋๋ง๋ค ๊ฐ์ฒด ์์ฑ๋๋์ง ํ ์คํธ
public class SingletonTest {
@Test
@DisplayName("์คํ๋ง ์๋ ์์ํ DI ์ปจํ
์ด๋")
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);
}
}
- ์ฐ๋ฆฌ๊ฐ ๋ง๋ค์๋ ์คํ๋ง ์๋ ์์ํ DI ์ปจํ
์ด๋์ธ AppConfig๋ ์์ฒญ์ ํ ๋ ๋ง๋ค ๊ฐ์ฒด๋ฅผ ์๋ก ์์ฑํ๋ค.
- ๊ณ ๊ฐ ํธ๋ํฝ์ด ์ด๋น 100์ด ๋์ค๋ฉด ์ด๋น 100๊ฐ ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ณ ์๋ฉธ๋๋ค! -> ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ์ฌํ๋ค
- ํด๊ฒฐ๋ฐฉ์์ ํด๋น ๊ฐ์ฒด๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๊ณ , ๊ณต์ ํ๋๋ก ์ค๊ณํ๋ฉด ๋๋ค.(์ฑ๊ธํค ํจํด)
2. ์ฑ๊ธํค ํจํด
์ฑ๊ธํค ํจํด
- ํด๋์ค์ ์ธ์คํด์ค๊ฐ ๋ฑ 1๊ฐ๋ง ์์ฑ๋๋ ๊ฒ์ ๋ณด์ฅํ๋ ๋์์ธ ํจํด
- ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ 2๊ฐ ์ด์ ์์ฑํ์ง ๋ชปํ๊ฒ ๋ง์์ผํจ
-> private ์์ฑ์๋ฅผ ์ฌ์ฉํด์ ์ธ๋ถ์์ ์์๋ก new ํค์๋๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๋๋ก ํจ
์ฑ๊ธํค ์์ฑ
ใด (test)hello.cored.singleton
public class SingletonService {
//1. static ์์ญ์ ๊ฐ์ฒด๋ฅผ ๋ฑ 1๊ฐ๋ง ์์ฑํด๋๋ค.
private static final SingletonService instance = new SingletonService();
//2. public์ผ๋ก ์ด์ด์ ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ํ์ํ๋ฉด ์ด static ๋ฉ์๋๋ฅผ ํตํด์๋ง ์กฐํํ๋๋ก ํ์ฉํ๋ค.
public static SingletonService getInstance() {
return instance;
}
//3. ์์ฑ์๋ฅผ private์ผ๋ก ์ ์ธํด์ ์ธ๋ถ์์ new ํค์๋๋ฅผ ์ฌ์ฉํ ๊ฐ์ฒด ์์ฑ์ ๋ชปํ๊ฒ ๋ง๋๋ค.
private SingletonService() {
}
public void logic() {
System.out.println("์ฑ๊ธํค ๊ฐ์ฒด ๋ก์ง ํธ์ถ");
}
}
- static ์์ญ์ ๊ฐ์ฒด instance๋ฅผ ๋ฏธ๋ฆฌ ํ๋ ์์ฑํด์ ์ฌ๋ ค๋๋ค
- ์ด ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ํ์ํ๋ฉด ์ค์ง getInstance() ๋ฉ์๋๋ฅผ ํตํด์๋ง ์กฐํํ ์ ์๋ค.
์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํญ์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๋ฐํ
- ๋ฑ 1๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ง ์กด์ฌํด์ผ ํ๋ฏ๋ก, ์์ฑ์๋ฅผ private์ผ๋ก ๋ง์์ ํน์๋ผ๋ ์ธ๋ถ์์ new ํค์๋๋ก ๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ ๊ฒ์ ๋ง๋๋ค
ํ ์คํธ
@Test
@DisplayName("์ฑ๊ธํค ํจํด์ ์ ์ฉํ ๊ฐ์ฒด ์ฌ์ฉ")
public void singletonServiceTest() {
//private์ผ๋ก ์์ฑ์๋ฅผ ๋ง์๋์๋ค. ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. //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.logic();
}
* ์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์ฐ๋ฉด ์๋์ผ๋ก ์ฑ๊ธํค ํจํด์ผ๋ก ๋ง๋ค์ด์ ๊ด๋ฆฌํจ
* ์ฑ๊ธํค ํจํด ๋ฌธ์ ์
- ๊ตฌํ ์ฝ๋ ๋ง์์ง
- ์์กด๊ด๊ณ์ ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฒด ํด๋์ค ์์กด (DIP ์๋ฐ)
- ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฒดํด๋์ค ์์กดํด์ OCP ์๋ฐ
- ํ ์คํธํ๊ธฐ ์ด๋ ค์
- ๋ด๋ถ ์์ฑ ๋ณ๊ฒฝ ๋ฐ ์ด๊ธฐํ ์ด๋ ค์
- private ์์ฑ์๋ก ์์ ํด๋์ค ๋ง๋ค๊ธฐ ์ด๋ ต๋ค
-> ์ ์ฐ์ฑ์ด ๋จ์ด์ง (์ํฐํจํด์ผ๋ก ๋ถ๋ฆฌ๊ธฐ๋ ํจ)
3. ์ฑ๊ธํค ์ปจํ ์ด๋
์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ทธ๋ฅดํค ํจํด ๋ฌธ์ ์ ํด๊ฒฐํ๋ฉด์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌ
์ฑ๊ธํค ์ปจํ ์ด๋
- ์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ํจํด์ ์ ์ฉํ์ง ์์๋ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ฑ๊ธํค์ผ๋ก ๊ด๋ฆฌ
(์คํ๋ง ๋น ๊ฐ์ฒด ํ๋๋ง ์์ฑํด์ ๊ด๋ฆฌ)
- ์คํ๋ง ์ปจํ ์ด๋๋ ์ฑ๊ธํค ์ปจํ ์ด๋ ์ญํ (์ฑ๊ธํค ๊ฐ์ฒด ์์ฑ ๋ฐ ๊ด๋ฆฌ = ์ฑ๊ธํค ๋ ์ง์คํธ๋ฆฌ)
- ์คํ๋ง ์ปจํ ์ด๋์ ์ด๋ฐ ๊ธฐ๋ฅ ๋๋ถ์ ์ฑ๊ธํด ํจํด์ ๋ชจ๋ ๋จ์ ์ ํด๊ฒฐํ๋ฉด์ ๊ฐ์ฒด๋ฅผ ์ฑ๊ธํค์ผ๋ก ์ ์ง ๊ฐ๋ฅ
(๊ตฌํ ์ฝ๋ ๋ง์์ง์ง ์๊ณ / DIP, OCP, private ์ผ๋ก ๋ถํฐ ์์ ๋กญ๊ฒ ์ฑ๊ธํค ์ฌ์ฉ ๊ฐ๋ฅ)
์คํ๋ง ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ๋ ํ ์คํธ ์ฝ๋
@Test
@DisplayName("์คํ๋ง ์ปจํ
์ด๋์ ์ฑ๊ธํค")
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);
}
}
์ฑ๊ธํค ์ปจํ ์ด๋ ์ ์ฉ ํ
- ์์ฒญ ์ฌ๋๋ง๋ค ์ด๋ฏธ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๊ณต์ ํด์ ํจ์จ์ ์ผ๋ก ์ฌ์ฌ์ฉ ๊ฐ๋ฅ
4. ์ฑ๊ธํค ๋ฐฉ์์ ์ฃผ์์
- ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ํ๋๋ง ์์ฑํด์ ๊ณต์ ํ๋ ์ฑ๊ธํค ๋ฐฉ์์ ์ฌ๋ฌ ํด๋ผ์ด์ธํธ๊ฐ ํ๋์ ๊ฐ์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ ์ฑ๊ธํค ๊ฐ์ฒด๋ ์ํ๋ฅผ ์ ์งํ๊ฒ(stateful) ์ค๊ณํ๋ฉด ์๋๋ค.
- ๋ฌด์ํ๋ก ์ค๊ณ (stateless)
> ํน์ ํด๋ผ์ด์ธํธ์ ์์กด์ ์ธ ํ๋๊ฐ ์์ผ๋ฉด ์๋จ
> ํน์ ํด๋ผ์ด์ธํธ๊ฐ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ ํ๋๊ฐ ์์ผ๋ฉด ์๋จ
> ๊ฐ๊ธ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํ๋๋ก
> ํ๋ ๋์ ์ ์๋ฐ์์ ๊ณต์ ๋์ง ์๋ ์ง์ญ๋ณ์, ํ๋ผ๋ฏธํฐ, ThreadLocal ๋ฑ์ ์ฌ์ฉ
-> ์คํ๋ง ๋น์ ํ๋์ ๊ณต์ ๊ฐ์ ์ค์ ํ๋ฉด ํฐ ์ฅ์ ๊ฐ ๋ฐ์ํ ์๋ ์๋ค!
์ํ๋ฅผ ์ ์งํ ๊ฒฝ์ฐ ๋ฐ์ํ๋ ๋ฌธ์ ์ (์์)
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;
}
}
public 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);
//ThreadA: A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ statefulService1.order("userA", 10000);
statefulService1.order("userA", 10000);
//ThreadB: B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ statefulService2.order("userB", 20000);
statefulService1.order("userB", 20000);
//ThreadA: ์ฌ์ฉ์A ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int price = statefulService1.getPrice();
//ThreadA: ์ฌ์ฉ์A๋ 10000์์ ๊ธฐ๋ํ์ง๋ง, ๊ธฐ๋์ ๋ค๋ฅด๊ฒ 20000์ ์ถ๋ ฅ System.out.println("price = " + price);
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
๋ฌด์ํ๋ก ์ค๊ณ!
public class StatefulService {
private int price; //์ํ๋ฅผ ์ ์งํ๋ ํ๋
public void order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
//์ฌ๊ธฐ๊ฐ ๋ฌธ์ !
//this.price = price;
// ์ถ๊ฐ
return price;
}
}
//ThreadA: A์ฌ์ฉ์ 10000์ ์ฃผ๋ฌธ statefulService1.order("userA", 10000);
int userAPrice = statefulService1.order("userA", 10000);
//ThreadB: B์ฌ์ฉ์ 20000์ ์ฃผ๋ฌธ statefulService2.order("userB", 20000);
int userBPrice = statefulService1.order("userB", 20000);
//ThreadA: ์ฌ์ฉ์A ์ฃผ๋ฌธ ๊ธ์ก ์กฐํ
int price = statefulService1.getPrice();
5. @Configuration๊ณผ ์ฑ๊ธํค
AppConfig
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
...
}
- ๊ฒฐ๊ณผ์ ์ผ๋ก MemboryMemberRepository๊ฐ ๊ฐ๊ฐ ๋ค๋ฅด๊ฒ ์์ฑ๋๋ฉด์ ์ฑ๊ธํค์ด ๊นจ์ง๋ ๊ฒ์ฒ๋ผ ๋ณด์
ํ ์คํธ
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
//ํ
์คํธ ์ฉ๋
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
//ํ
์คํธ ์ฉ๋
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService",OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository",MemberRepository.class);
//๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฐธ๊ณ ํ๊ณ ์๋ค.
System.out.println("memberService -> memberRepository = " + memberService.getMemberRepository());
System.out.println("orderService -> memberRepository = " + orderService.getMemberRepository());
System.out.println("memberRepository = " + memberRepository);
//๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฐธ๊ณ ํ๊ณ ์๋ค.
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
- memberRepository ์ธ์คํด์ค๋ ๋ชจ๋ ๊ฐ์ ์ธ์คํด์ค๊ฐ ๊ณต์ ๋์ด ์ฌ์ฉ๋จ
- ๋๋ฒ ํธ์ถ์ด ์๋๋ ๊ฑด๊ฐ? -> ํ๋ฒ๋ง ํธ์ถ๋จ = ์ฑ๊ธํค ๋ณด์ฅํด์ค๋ค๋ ๋ง
6. @Configuration๊ณผ ๋ฐ์ดํธ์ฝ๋ ์กฐ์์ ๋ง๋ฒ
์คํ๋ง์ ํด๋์ค์ ๋ฐ์ดํธ์ฝ๋ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//AppConfig๋ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋๋ค.
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
//์ถ๋ ฅ: bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
}
- ์์ ํด๋์ค๋ผ๋ฉด class hello.core.Appconfig๋ผ๊ณ ์ถ๋ ฅ๋์ด์ผํ์ง๋ง
- bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70 ์ผ๋ก ๋ธ
- ์คํ๋ง์ด CGLIB์ด๋ผ๋ ๋ฐ์ดํธ์ฝ๋ ์กฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ AppConfig ํด๋์ค๋ฅผ ์์๋ฐ์ ์์์ ๋ค๋ฅธ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ๊ทธ ๋ค๋ฅธ ํด๋์ค๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก!
AppConfig@CGLIB ์์์ฝ๋
@Bean
public MemberRepository memberRepository() {
if (memoryMemberRepository๊ฐ ์ด๋ฏธ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก๋์ด ์์ผ๋ฉด?) {
return ์คํ๋ง ์ปจํ
์ด๋์์ ์ฐพ์์ ๋ฐํ;
} else { //์คํ๋ง ์ปจํ
์ด๋์ ์์ผ๋ฉด
๊ธฐ์กด ๋ก์ง์ ํธ์ถํด์ MemoryMemberRepository๋ฅผ ์์ฑํ๊ณ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก return ๋ฐํ
}
}
- @Bean์ด ๋ถ์ ๋ฉ์๋๋ง๋ค ์ด๋ฏธ ์คํ๋ง ๋น์ด ์กด์ฌํ๋ฉด ์กด์ฌํ๋ ๋น์ ๋ฐํํ๊ณ , ์คํ๋ง ๋น์ด ์์ผ๋ฉด ์์ฑํด์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๊ณ ๋ฐํํ๋ ์ฝ๋๊ฐ ๋์ ์ผ๋ก ๋ง๋ค์ด์ง๋ค.
- @Bean๋ง ์ฌ์ฉํด๋ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋์ง๋ง, ์ฑ๊ธํค์ ๋ณด์ฅํ์ง ์๋๋ค.
- ํฌ๊ฒ ๊ณ ๋ฏผํ ๊ฒ์ด ์๋ค. ์คํ๋ง ์ค์ ์ ๋ณด๋ ํญ์ @Configuration ์ ์ฌ์ฉํ์.
'๐จโ๐ป Web Development > Spring - ๊ธฐ๋ณธ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] 7.๋ค์ํ ์์กด๊ด๊ณ ์ฃผ์ ๋ฐฉ๋ฒ (0) | 2023.02.28 |
---|---|
[Spring] 6.์ปดํฌ๋ํธ ์ค์บ (0) | 2023.02.24 |
[Spring] 4.์คํ๋ง ์ปจํ ์ด๋์ ์คํ๋ง ๋น (0) | 2023.02.20 |
[Spring] 3.์คํ๋ง ํต์ฌ ์๋ฆฌ ์ดํด2 - ๊ฐ์ฒด ์งํฅ ์๋ฆฌ ์ ์ฉ (0) | 2023.02.18 |
[Spring] 2.์คํ๋ง ํต์ฌ ์๋ฆฌ ์ดํด1 - ์์ ๋ง๋ค๊ธฐ (0) | 2023.02.17 |
์ต๊ทผ๋๊ธ