๐ ์ฑ๊ธํค์ด๋?
์ฑ๊ธํค์ ์ธ์คํด์ค๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค์ง ํ๋๋ง ์์ฑํ ์ ์๋ ํด๋์ค๋ฅผ ์๋ฏธํ๋ค.
์๋ก ์คํ๋ง ๊ธฐ๋ณธ ๋น์ด ์ฑ๊ธํค์ด๋ค. ์คํ๋ง ๋น์ ์ฑ๊ธํค์ผ๋ก ์ฌ์ฉํจ์ผ๋ก์จ ์ด์ ์
๋ชจ๋ ํด๋ผ์ด์ธํธ ๋ณ๋ก ์ธ์คํด์ค๋ฅผ ์์ฑํ ์ด์ ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ฌด์๋ฏธํ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๋ฅผ ์ค์ผ ์ ์๋ค.
๋ณ๊ฐ๋ก ์คํ๋ง์์์ ๋น ์ฑ๊ธํค์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ์ฑ๊ธํค์ด ์๋
์คํ๋ง ApplicationContext ๋ด์์์ ์ฑ๊ธํค์ ์๋ฏธํ๋ค.
์ฆ, new XXXService();๋ฅผ ํ๋ฉด ์๋ก์ด ๊ฐ์ฒด๊ฐ Context ๋ฐ์ ์์ฑ์ด ๋๋ค๋ ์๋ฏธ์ด๋ค.
๐ ์ฑ๊ธํค์ ์ฃผ์์
์ฑ๊ธํค์ ๋ชจ๋ ํด๋ผ์ด์ธํธ๊ฐ ๊ณต์ ํ๋ ์์์ด๊ธฐ ๋๋ฌธ์ ์ธ์คํด์ค ๋ณ์๊ฐ ์กด์ฌํ๋ฉด ์๋๋ค.
๋ง์ฝ ์ธ์คํด์ค ๋ณ์๋ฅผ ์๋์น ์๊ฒ ํ๋์ ํด๋ผ์ด์ธํธ์์ ๋ณ๊ฒฝํ๊ฒ ๋๋ฉด ๋ชจ๋ ํด๋ผ์ด์ธํธ์๊ฒ ์ ํ๋๊ธฐ
๋๋ฌธ์ด๋ค.
๐ ์ฑ๊ธํค์ ๋จ์
ํ ์คํธ ์ mock์ผ๋ก ๋์ฒดํ๊ธฐ๊ฐ ์ด๋ ต๋ค. ๊ทธ๋์ ์คํ๋ง์์๋ ์์ฑ์ ์ฃผ์ ์ ๊ถ์ฅํ๋ค.
๐ ์ฑ๊ธํค ๋ณด์ฆ ๋ฐฉ๋ฒ 2๊ฐ์ง
์ฑ๊ธํค์ ๊ตฌํํ๋ ๋ฐฉ์์ ํฌ๊ฒ ๋ ๊ฐ์ง์ด๋ค.
- public static final ํ๋ ๋ฐฉ์์ ์ฑ๊ธํค
- ์ ์ ํฉํ ๋ฆฌ ๋ฐฉ์์ ์ฑ๊ธํค
๐ public static final ํ๋ ๋ฐฉ์
์ด ๋ฐฉ์์ public static final๋ก ๋ฏธ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , private์ผ๋ก ๋ ์ด์ ์์ฑํ์ง ๋ชปํ๊ฒ ๋ง์์
์ฑ๊ธํค์์ ๋ณด์ฆํ๋ ๋ฐฉ์์ด๋ค.
public class Elvis {
private Elvis() {}
public static final Elvis INSTANCE = new Elvis();
public void method() {
System.out.println("Elvis.method");
}
}
๐ฅฌ ๋ฆฌํ๋ ์ ๋ฌธ์ ์
ํ์ง๋ง ์ด ๋ฐฉ์์ ๋ฆฌํ๋ ์ ์ผ๋ก ์ ๊ทผํ๋ฉด private ์์ฑ์์ ์ ๊ทผํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์๋ ์ฝ๋์ when์ ์ฒ๋ผ reflection์ ์ฌ์ฉํ๋ฉด ์ฑ๊ธํค์ด ๋ณด์ฅ๋์ง ์๋ ํ์์ด ๋ฐ์ํ๊ฒ ๋๋ค.
@Test
void ๋ฆฌํ๋ ์
์ผ๋ก_private_์์ฑ์๋ก_์ธ์คํด์ค_์์ฑ()
throws
NoSuchMethodException,
InvocationTargetException, InstantiationException, IllegalAccessException {
// given
Elvis instance = Elvis.INSTANCE;
// when
Constructor<Elvis> constructor = Elvis.class.getDeclaredConstructor();
constructor.setAccessible(true);
Elvis reflectionInstance = constructor.newInstance();
// then
assertThat(instance).isNotSameAs(reflectionInstance);
}
ํด๊ฒฐ ๋ฐฉ๋ฒ
์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๊ฒ ์ง๋ง, ์ฑ ์์ ์๊ฐํ๋ ๋ด์ฉ์ ์์ฑ์์์ ๊ฐ์ฒด๊ฐ ๋ ์์ฑ๋๋ ค ํ๋ฉด,
์์ธ๋ฅผ ๋์ง๋ ๋ฐฉ๋ฒ์ ์๋ดํ๊ณ ์๋ค. ๊ทธ ๋ฐฉ๋ฒ์ ์ฑ ์ ์์ง๋ ์์ง๋ง, ์๋์ ๊ฐ์ด ํด๋ณผ ์ ์์ ๊ฒ ๊ฐ๋ค.
private Elvis() {
if (Objects.nonNull(INSTANCE)) {
throw new RuntimeException("์ฑ๊ธํค์ด๋ฏ๋ก ์์ฑ ๋ถ๊ฐ!!");
}
}
์ด๋ฅผ ํ ์คํธ ํด๋ณด๋ฉด Runtime Exception์ผ๋ก ๋ฆฌํ๋ ์ ๊ณต๊ฒฉ์ด ์คํจํ๋ ๊ฒ์ ์ ์ ์๋ค.
@Test
void ๋ฆฌํ๋ ์
_๋ฐฉ์ด()
throws
NoSuchMethodException,
InvocationTargetException, InstantiationException, IllegalAccessException {
// given
Elvis instance = Elvis.INSTANCE;
// when
Constructor<Elvis> constructor = Elvis.class.getDeclaredConstructor();
constructor.setAccessible(true);
// then
constructor.newInstance(); // RutimeException
}
ํ์ง๋ง Assertj๋ฅผ ์ฌ์ฉํด์ ๊ฒ์ฆ์ ํ๋ฉด ์กฐ๊ธ ๋ค๋ฅด๊ฒ InvocationTargetException์ ๋ฐ์ ์ํจ๋ค.
@Test
void ๋ฆฌํ๋ ์
_๋ฐฉ์ด()
throws
NoSuchMethodException,
InvocationTargetException, InstantiationException, IllegalAccessException {
// given
Elvis instance = Elvis.INSTANCE;
// when
Constructor<Elvis> constructor = Elvis.class.getDeclaredConstructor();
constructor.setAccessible(true);
// then
assertThatThrownBy(constructor::newInstance)
.isInstanceOf(InvocationTargetException.class);
}
java doc์์๋ InvocationTargetException์ ์๋์ ๊ฐ์ด ์ค๋ช ํ๊ณ ์๋ค.
InvocationTargetException์ ํธ์ถ๋ ๋ฉ์๋ ๋๋ ์์ฑ์๊ฐ ๋์ง ์์ธ๋ฅผ ๋ํํ๋ ์ ํ๋ ์์ธ์ ๋๋ค.
๐ฅฌ ์ฅ์
- ์ฝ๋๊ฐ ์์ฃผ ๊ฐ๊ฒฐํ๋ค.
๐ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋ ๋ฐฉ์
์ด ๋ฐฉ์์ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ํ์ฉํด์ ๋ฉ์๋ ๋ด๋ถ์์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ ๋ฐฉ์์ด๋ค.
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {}
public static Elvis getInstance() {
return INSTANCE;
}
}
๐ฅฌ API๋ฅผ ๋ฐ๊พธ์ง ์๊ณ ๋ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ๋ค.
์ฑ๊ธํค์ด ์๋๋๋ก ๋ณ๊ฒฝํ ์ ์๋ค.
public static final ํ๋ ๋ฐฉ์์์ ์ฑ๊ธํค์ด ์๋๋๋ก ์ฌ์ฉํ๋ ค๋ฉด ํด๋ผ์ด์ธํธ ์ฝ๋๊ฐ ๋ณ๊ฒฝ ๋์ด์ผ ํ๋ค.
์๋์ ์ฝ๋๊ฐ public static final์ ์ฝ๋์ธ๋ฐ ํด๋ผ์ด์ธํธ ์ฝ๋๋ Evlis.INSTANCE๋ฅผ ์ฐธ์กฐํ๊ณ
์์์ง๋ง, new Elvis()๋ฅผ ์ฌ์ฉํ๋๋ก ์ฝ๋๊ฐ ๋ณ๊ฒฝ ๋์ด์ผ ํ๋ค.public static final Elvis INSTANCE = new Elvis();
์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋ ๋ฐฉ์์ ๋ฉ์๋ ๋ด๋ถ๋ง ์์ ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ ์ฝ๋ ๋ณ๊ฒฝ์์ด
๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ๋ค.public static Elvis getInstance() { return new Elvis(); }
๐ฅฌ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ์ ๋ค๋ฆญ ์ฑ๊ธํค ํฉํ ๋ฆฌ๋ก ๋ง๋ค ์ ์๋ค. ??????
๐ฅฌ Supplier๋ก ์ฌ์ฉํ ์ ์๋ค.
์๋ ์ฝ๋์ ๊ฐ์ด Supplier๋ก ์ฌ์ฉ๋ ์ ์๋ค๋ ์๋ฏธ์ธ๋ฐ
์ด๋ค ์ํฉ์์ Supplier์ ๊ฐ์ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค๊ณ ํ ๊ฒฝ์ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค๋ ์๋ฏธ์ด๋ค.
Supplier<Elvis> elvisSupplier = Elvis::getInstance;
๐ฅฌ ๋จ์
์ํฉ์ ๋ง์ถฐ ์ฐ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ฑํ ๋จ์ ์ด๋ผ๊ณ ๋ณผ๊ฑด ์์ง๋ง ์๋ฌด๋๋
public static final ๋ฐฉ์์ ๋นํด์๋ ๊ฐ๊ฒฐํ์ง ์๋ค๋ ์ ์ด๋ค.
์ ๋ฆฌ
ํ์ง๋ง ๊ฒฐ๊ตญ ์ด๋ฌํ ์ฅ์ ๋ค์ด ํ์์๋ค๋ฉด public static final ๋ฐฉ๋ฒ์ ์ฐ๋ ๊ฒ์ด ํจ์ฌ ๊ฐ๊ฒฐํ๋ค.
๐ ์ง๋ ฌํ ์ฃผ์์
์์์ ์ดํด๋ณธ ๋ ๋ฐฉ๋ฒ์ ๋ชจ๋ ์ง๋ ฌํ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ์ญ์ง๋ ฌํ ํ ๋ ๊ฐ์ ํ์ ์ ์ธ์คํด์ค๊ฐ
์ฌ๋ฌ๊ฐ ์๊ธธ ์ ์๋ค. ์ฆ, ์ฑ๊ธํค์ด ๊นจ์ง๋ค. ๊ทธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ์ธ์คํด์ค ํ๋์
transient(์ง๋ ฌํ ๋ชฉ๋ก์์ ์ ์ธ)๋ฅผ ์ถ๊ฐํ๊ณ readResolve() ๋ฉ์๋๋ฅผ ์๋์ ๊ฐ์ด ๊ตฌํํ๋ฉด ๋๋ค.
์ ํํ์ง๋ ์์ง๋ง ๋๋ต์ ์ผ๋ก Serializable ์ธํฐํ์ด์ค์์ readResolve๋ฅผ ์ด์ฉํด์ ์ญ์ง๋ ฌํ๋ฅผ
ํ๋ค๊ณ ํ๋๋ฐ ์ด๋ ๊ฒ ์ค๋ฒ๋ผ์ด๋ฉํ๋ฉด, ์ค๋ฒ๋ผ์ด๋ฉ๋ ๋ฉ์๋๋ฅผ ์ฝ๊ธฐ ๋๋ฌธ์ ์ญ์ง๋ ฌํ์ ์์ ํ๋ค๊ณ ํ๋ค.
์ค์ ๋ก Serializable์ ๋น ์ธํฐํ์ด์ค์ด์ง๋ง ์ค์ ๋ก๋ ์ด๋ค ๋ฉ์๋๊ฐ ์กด์ฌํ๋ค๊ณ ํ๋ค. ๊ทธ ์ค ํ๋๊ฐ
๋ฐ๋ก readResolve()์ธ ๊ฒ์ด๋ค.
public static transient final Elvis INSTANCE = new Elvis();
@Serial
public Object readResolve() {
return INSTANCE;
}
@Serial: ์ง๋ ฌํ ์ญ์ง๋ ฌํ์ ์ค๋ฒ๋ผ์ด๋ฉ ๋ฉ์๋๊ฐ ๋ง๋์ง ํ์ธํด์ฃผ๋ ์ ๋ ธํ ์ด์ ์ด๋ค.
๋ง์ฝ readRsolve1()์ด๋ผ๊ณ ์ ์ธํ๋ฉด ์๋์ ๊ฐ์ warning์ ๋์ด๋ค.Annotated member is not a part of the serialization mechanism
๐ readResolve ๋ฉ์๋
ํด๋น ๋ด์ฉ์ ์ฑ๊ธํค์ ๊ธฐ์ค์ผ๋ก ์ค๋ช ํ๋ค.
readResolve ๋ฉ์๋๋ ๋ฌด์์ธ๊ฐ
์ฐธ๊ณ : oracle document
์ญ์ง๋ ฌํ ์ readResolve ๋ฉ์๋๋ฅผ ์ ์ธํ๋ค๋ฉด, ์ธ์คํด์ค๋ฅผ ์ง์ ํต์ ํ ์ ์๋ค.
์ธ์คํด์ค๋ฅผ ํต์ ํ๋ค๋ ๊ฒ์ ์ฑ๊ธํค์ ์๋ก ๋ค ์ ์๋ค.
์ญ์ง๋ ฌํ ์ readResolve ๋ฉ์๋๊ฐ ์ ์ธ๋์ง ์์ผ๋ฉด ๋งค๋ฒ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
ํ์ง๋ง ์ ์ธ๋์ด ์๋ค๋ฉด, ๋ฉ์๋๊ฐ ๋ฐํํ๋ ๊ฐ์ ์ฌ์ฉํ๊ฒ ๋๋ค.
๊ทธ ์๋ฆฌ๋ ObjectInpuStream์ด ์๋์ ๊ฐ์ ํ๋ก์ธ์ค๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค.
- ์คํธ๋ฆผ์ ์ญ์ง๋ ฌํํ๊ธฐ ์ํด ๋ฐํํ ์ค๋น๋ฅผ ํ๋ค.
- readResolve ๋ฉ์๋๊ฐ ์ ์ธ๋์ด ์๋์ง ํ์ธํ๋ค.
- ์ ์๋ ๊ฒฝ์ฐ readResolve ๋ฉ์๋๊ฐ ๋ฐํํ๋ ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๋ฐํํ๋๋ก ํ๋ค.
- ๊ทธ๋ ๋ค๊ณ ํด๋ ์๋ก์ด ๊ฐ์ฒด๋ ์์ฑ์ด๋๋๋ฐ ์ด๋ GC ๋์์ด ๋๋ค.
trasient๋ฅผ ์ ์ ์ธํด์ผ ํ๋๊ฐ
ํ์์๋ค๋ฉด ์ ๊ฑฐํ๋ฉด ๋๋ค. ํ์ ์๋ค๋ฉด ์ ์ธํ๊ณ ์ง๋ ฌํ ์ญ์ง๋ ฌํ๋ฅผ ํ๋ฉด ๋๋ค.
ํ์ง๋ง ํต์ ๋ฅผ ์ํด์ readResolve ๋ฉ์๋๋ฅผ ์ ์ธํด์ ํต์ ํ๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์์ผ๋ฉด ๋๋์ฒด ์ง๋ ฌํ๋ฅผ ์ ํ๋๊ฐ?์ ๋ํ ๊ณ ๋ฏผ์ด ์์๋ค.
์ง๋ ฌํ๋ผ๋๊ฒ ๊ฒฐ๊ตญ ํ๋๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ํ๋ ๊ฒ์ธ๋ฐ ์๋ก Json์ด ๋๊ฒ ๋ค.
๊ทธ๋ ๋ค๋ฉด ํ๋๋ฅผ ์ง๋ ฌํ ํ์ง ์๋๋ค๋ฉด ์ ์ง๋ ฌํ ์ญ์ง๋ ฌํ๋ฅผ ํ ๊น?
์๋ฅผ ํ๋ ๋ค์ด๋ณด๋ฉด AMQP๊ฐ ๋ ์ ์๋ค. AMQP์ ๋ ์๋ฒ์ ํต์ ์ ์ ์ฐจ๋ก ์์๋ณด์.
- producer ์๋ฒ๋ ์ง๋ ฌํํด์ AMQP์ ๋ณด๋ธ๋ค.
- ์ปจ์๋จธ ์๋ฒ๋ AMQP์์ pullํ๋ค.
- ์ปจ์๋จธ ์๋ฒ๋ ์ด๋ฅผ ์ญ์ง๋ ฌํ ํ๋ค.
3๋ฒ ์ ์ฐจ์์ ๋ง์ฝ ์ปจ์๋จธ๋ ๋ฐ์ดํฐ๊ฐ ํ์์๋ ๊ฒ์ด ์๋๊ณ ๊ทธ๋ฅ ์ปจ์จ๋ง ํ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ผ๋ฉด
์ฌ์ฉํ ์ ์๋ค. ์ด๋ readResolve ๋ฉ์๋๋ฅผ ์ ์ธํ๋ฉด ์บ์ฑ๋ ๊ฐ์ ์ฌ์ฉํ๋๋ก ํ ์ ์๋ค.
readResolve ๋ฉ์๋๋ ์ด๋์ ์๋๊ฐ
์กด์ฌํ๋ ๋ฉ์๋๊ฐ ์๋๊ณ ๋ฆฌํ๋ ์ ์ ํ์ฉํ ๊ฒ์ด๋ค.
ObjectInpuStream์ด ์ญ์ง๋ ฌํ ๊ด๋ จ ํด๋์ค๋ก ์ฌ๊ธฐ์ ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ๋ค.
private Object readOrdinaryObject(boolean unshared)๋ผ๋ ๋ฉ์๋๊ฐ
๋ด๋ถ์ ์ผ๋ก invokeReadResolve()๋ฅผ ํ๋๋ฐ ์ด๋ ObjectStreamClass.java์ ์กด์ฌํ๋ค.
๋ํ ์๋ ์ฝ๋์ ๊ฐ์ด ObjectStreamClass.java์์๋ ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
ํ๋ ๋ ์ ์ ์๋๊ฒ์ readObject๋ฉ์๋์ธ๋ฐ ์ด ๋ฉ์๋๊ฐ ๊ฒฐ๊ตญ์ readResolve๋ฅผ ํธ์ถํ๊ฒ ๋๋ค.
์ฆ, readResolve๊ฐ ์๋์ง ์ฒดํฌํ๊ณ ์์ผ๋ฉด ์ฌ์ฉํ๊ณ ์์ผ๋ฉด ์ฌ์ฉํ์ง ์๋ ๊ฒใ ๋ใ .
๐ ์์๊ฐ ํ๋์ธ enum
์ฑ ์์๋ ๊ฐ์ฅ ๋ฐ๋์งํ ๋ฐฉ์์ด๋ผ๊ณ ํ๋ค.
๊ฐ์ธ์ ์ผ๋ก ๊ฐ์ฅ ์ด์์ ์ธ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ๋ ์๊ฐํ์ง ์๋๋ค.
์์๋ ์๋๊ณ ๊ตฌํ๋ ์๋๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฅ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์คํ๋ง์ฒ๋ผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์๋ณด์ธ๋ค.
์ฆ, ์ ์ฐ์ฑ์ด ๋จ์ด์ง๋ค.
public enum Elvis {
INSTANCE;
public void method() {
System.out.println("Elvis.method");
}
}
๐ฅฌ ์ฅ์
- ์ญ์ง๋ ฌํ์ ์ฑ๊ธํค์ด ๋ณด์ฅ๋๋ค.
- ๋ฆฌํ๋ ์ ๊ณต๊ฒฉ๋ ๋ง์์ค๋ค.
๐ฅฌ ๋จ์
- ์์์ด ๋ถ๊ฐ๋ฅํ๋ค.