๋ฐ์ดํธ์ฝ๋ ์กฐ์(๋ฆฌํ๋ ์ , ๋ค์ด๋๋ฏน ํ๋ก์, ์ ๋ ธํ ์ด์ ํ๋ก์ธ์)
by JiwonDev์คํ๋ง์์ ๋ค์ํ ์ค์ ๊ธฐ๋ฅ๋ค (@Autowried ๋ฑ)์ ์ด๋ป๊ฒ ์ฝ๋์์ด ๊ฐ์ฒด๋ฅผ ์ฃผ์ ํ๊ณ , ์กฐ์ํ ์ ์๋ ๊ฒ์ผ๊น? ๋ Lombok๊ฐ์ ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ด๋ป๊ฒ ๋์ํ๋ ๊ฒ์ผ๊น? ๊ทธ ๋น๋ฐ์ ๋ชจ๋ ์๋ฐ์ ๋ฐ์ดํธ์ฝ๋์ ์๋ค.
2021.08.16 - [๊ธฐ๋ณธ ์ง์/Java ๊ธฐ๋ณธ์ง์] - JVM์ ์๋ฆฌ์ ๋ฐ์ดํธ์ฝ๋
Java Reflection - Dynamic Proxies (jenkov.com)
# ์๋ฐ์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ฐฉ๋ฒ
๋ฆฌํ๋ ์ ๊ธฐ๋ฒ์ด ์๋๋ผ ๊ทธ๋ฅ ๋ฐ์ดํธ์ฝ๋ ์์ฒด๋ฅผ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๋ค.
- CGLib (Code Generator Library) - ๋ฆฌํ๋ ์ ์ ์ด์ฉํ ๋ค์ด๋๋ฏน ํ๋ก์ ์์ฑ๋๊ตฌ
- ASM ๋ฐ์ดํธ์ฝ๋ ์กฐ์ (low level) https://asm.ow2.io/
- Javassist https://www.javassist.org/
- ByteBuddy ๋ฐ์ดํธ์ฝ๋ ์กฐ์(ASM์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ฆ)https://bytebuddy.net/#
# ๋ฆฌํ๋ ์
์คํ๋ง์ Dependency Injection์ ์ด๋ป๊ฒ ๋์ํ ๊น?
@Service
public class BookService {
@Autowired // ์คํ๋ง์ ์ด๋ป๊ฒ bookRepository ์ธ์คํด์ค๋ฅผ ๋ฃ์ด ์ค ๊ฒ์ผ๊น?
BookRepository bookRepository;
}
@ ๋ฆฌํ๋ ์ ์ด๋?
๋ฆฌํ๋ ์ ์ด๋ [Class ์ ๋ณด๋ฅผ ๋ด๋ ๊ฐ์ฒด]๋ฅผ ์ด์ฉํด ํด๋น ํด๋์ค์ ์ ๋ณด๋ฅผ ๋ถ์ํด๋ด๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์๋ฏธํ๋ค. ์ฆ ๊ตฌ์ฒด์ ์ธ ํด๋์ค ํ์ , ์ ๋ณด๋ฅผ ์์ง ๋ชปํ๋๋ผ๋ ๊ฐ์ฒด ์ด๋ฆ("my.ClassName")์ผ๋ก ๋ฐํ์ ์์ ์์ [๋ฆฌํ๋ ์ ๊ฐ์ฒด์ ๋ฐ์ดํธ์ฝ๋]๋ฅผ ์ด์ฉํด ๋ด๊ฐ ์ํ๋ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ ๋ง์ด๋ค.
๋์ ๋ฐ์ธ๋ฉ์ด ๋์ง ์๋ ์๋ฐ์์, ๋ฆฌํ๋ ์ ๊ธฐ๋ฒ์ ์ด์ฉํด ๋ฐํ์์ ๋์ ์ธ ๋ฐ์ธ๋ฉ์ ์ ๊ณตํ๋ ๊ฒ์ด๋ผ๊ณ ์ดํดํ๋ฉด ์ฝ๋ค. ์๋ฐ์ Object ๊ฐ์ฒด์๋ Object.Class ๋ผ๋ ๋ฆฌํ๋ ์ ๊ฐ์ฒด(=ํด๋น Class์ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ๊ฐ์ฒด)๋ฅผ ๊ฐ์ง๊ณ ์๋๋ฐ, ์ปดํ์ผ ํ์์ ํด๋์ค์ ์ด๋ฆ์ ์ ์ ์๋ค๋ฉด ์ด Object.Class ๊ฐ์ฒด๋ฅผ ์ป์ ์ ์๋ค.
// Class.forName("ํจํค์ง ์ ์ฒด ๊ฒฝ๋ก")
// ํด๋์ค์ด๋ฆ.class
MyHello.class
Class.forName("mypackage.core.MyHello")
// ๋๋ ์ธ์คํด์ค์์ getClass()๋ก ๋ฐ์์ค๋ ๋ฐฉ๋ฒ๋ ์๋ค.
Book book = new Book();
Class<? extends Book> aClass = book.getClass();
Class myObjectClass = Class.forName("me.package.className"); // ํด๋์ค๋ฅผ ์์ฑ ํ ๋ฐํ
Class.forName("oracle.jdbc.driver.OracleDriver");
// ์ฐธ๊ณ :: Class.forName() ๋ฉ์๋ ์ ์
public static Class<?> forName(String className)
throws ClassNotFoundException {...}
@ ๋ฆฌํ๋ ์ ์ฌ์ฉํด๋ณด๊ธฐ, java.lang.Class<T>
public class App {
public static void main(String[] args) {
Class<User> userClass = User.class;
User user = new User("test", "name", "1234"); // ํ
์คํธ์ฉ ์ธ์คํด์ค
Arrays.stream(userClass.getDeclaredFields())
.forEach(filed -> {// User.class์ ์ ์๋ ํ๋๋ฅผ ๋ชจ๋ ๊ฐ์ ธ์์
try {
filed.setAccessible(true); // private ์ ๊ทผ์ ์ด์ ๋ฌด์
// filed.get(์ธ์คํด์ค)๋ก ํด๋น ์ธ์คํด์ค์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ ๋ฐํํจ.
// ๋ง์ฝ .get(null)์ ํ๋ค๋ฉด static์ธ ํด๋์ค ๋ฐ์ดํฐ๋ฅผ ๋ฐํํจ.
System.out.println(filed + " " + filed.get(user));
} catch (IllegalAccessException e) {
// ์๋ชป๋ ์ ๊ทผ, ํด๋น ๋ฉ์๋๊ฐ ์๋ ๊ฒฝ์ฐ๋ฑ๋ฑ..
}
});
}
}
@ ๋ฆฌํ๋ ์ ๊ณผ ์ด๋ ธํ ์ด์
๋์ฐ๋ฏธ๊ฐ ์ข๋ค๋ฉด ์์์ ๋ดค๊ฒ ์ง๋ง, Class<T>์๋ getAnnotation()์ด๋ผ๋ ๋ฉ์๋๊ฐ ์๋ค. ๊ทธ๋์ ํด๋์ค์ ์ด๋ ธํ ์ด์ ์ ๋ฌ๊ณ ์๋์ ๊ฐ์ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋ฉด ์ด๋ ธํ ์ด์ ๋ค์ด ๋์์ผํ ๊ฒ ๊ฐ์ง๋ง ์ค์ ๋ก๋ ์๋ฌด๋ฐ ๊ฐ๋ ์ฐํ์ง ์๋๋ค.
// ๋ฑ๋ก๋ ์ด๋
ธํ
์ด์
์ด ์์. ์ฝ์์ฐฝ์ ์ฐ์ด๋ ์๋ฌด๊ฒ๋ ์๋์จ๋ค.
Arrays.stream(User.class.getAnnotations()).forEach(System.out::println);
๊ทธ ์ด์ ๋ ์ด๋ ธํ ์ด์ ์์ฒด๊ฐ ์ฃผ์ (//)๊ณผ ๋์ผํ ์ทจ๊ธ์ ๋ฐ๊ธฐ ๋๋ฌธ์ด๋ค. ์ปดํ์ผ๋ฌ๋ฅผ ์ํ ์ฃผ์.
@MyAnnotation์ ์ ๋ณด๋ JVM์ด ์ฝ์ด๋ค์ฌ ์ฒ๋ฆฌํ๋ค. ์ฆ ์๋ฐ ์ฝ๋์ ๋ฐ์ดํธ์ฝ๋(.class)๊น์ง๋ ๋จ์ง๋ง ํด๋น ๋ฐ์ดํธ์ฝ๋๊ฐ ์ฝ์ด๋ค์ฌ์ง ํ์ ๋ฐํ์ ๋ฉ๋ชจ๋ฆฌ์๋ ์ด๋ ธํ ์ด์ ์ ๋ํ ์ด๋ ํ ์ ๋ณด๋ ๋จ์์์ง ์๋ค.
๋ค๋ง ์๋ฐ์์๋ ์ฝ๋์์ ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ ์ ์๋๋ก ์ด๋ ธํ ์ด์ ์ ์ ์ํ ๋ ์๋ช ์ฃผ๊ธฐ(@Retention)๋ฅผ ๋ฐํ์๊น์ง ๋จ์์๋๋ก ์ค์ ํ ์ ์๋ค. Class.getAnnotation()์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ด๋ ธํ ์ด์ ์ ๋ง๋ค๋ฉด ๋๋ค.
import java.lang.annation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME) // ๋ฐํ์์๋ ์ด๋
ธํ
์ด์
์ฌ์ฉ๊ฐ๋ฅ(๋ฆฌํ๋ ์
)
@Target( ElementType.TYPE, ElementType.FILED )
public @interface FunctionalInterface{...}
์ดํ getAnnotation()์ ์ฝ์์ ์ฐ์ด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ๋ ธํ ์ด์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ณ , ์ฌ์ฉํ ์ ์๋ค.
// ํด๋น ํด๋์ค์์ ์ฌ์ฉ๋ ๋ชจ๋ ์ด๋
ธํ
์ด์
์กฐํ
//ex:: @my.package.MyAnnotation(value="haha", number=100)
Arrays.stream(User.class.getAnnotations()).forEach(System.out::println);
// ํน์ ํ๋, ๋ฉ์๋์ ์๋ ์ด๋
ธํ
์ด์
๋ง ๊ฐ์ ธ์ฌ ์๋ ์๋ค.
Arrays.stream(User.class.getDeclaredFields()).forEach(
field -> {// ํด๋น ํ๋์ ์๋ ์ด๋
ธํ
์ด์
๋ค์ ๋ชจ๋ ๊บผ๋ธ๋ค
Arrays.stream(field.getAnnotations()).forEach(
annotation -> {// ์ด๋
ธํ
์ด์
์ ํ์
์ ๋น๊ตํด์ ์ฐพ์ ์ ์๋ค.
if (annotation instanceof MyAnnotation) {
MyAnnotation myAnno = (MyAnnotation) a;
System.out.println(myAnno.value() + " " + myAnno.myNumber());
}
});
}
);
@ ๋ฆฌํ๋ ์ API - ์ธ์คํด์ค ์์ฑ , ํด๋์ค ์์
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class App {
// ๋ฆฌํ๋ ์
์ฌ์ฉ์ ๋ฐ์ํ ์ ์๋ ์์ธ๋ค.
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//Class<User> userClass = User.class; ์ ๋ค๋ฆญ ์๋ต๊ฐ๋ฅ
Class userClass = Class.forName("my.package.User");
// (~) ์๋ ํ๋ผ๋ฉํ์ ํ์
์ ์ ๋๋ค. ๋ง์ฝ ์๋ค๋ฉด null
Constructor constructor = userClass.getConstructor(String.class, String.class, String.class);
User user = (User) constructor.newInstance("์์ฑ์์ ๋งค๊ฐ๋ณ์ ๋ฌธ์์ด๊ฐ", "name", "1234");
Field a = User.class.getDeclaredField("name"); // ํด๋์ค ๋ฉค๋ฒ(static ๋ฉค๋ฒ)๋ฅผ ๊ฐ์ ธ์จ๋ค.
a.setAccessible(true); // ์ ๊ทผ์ ์ด์(private) ๋ฅผ ๋ฌด์ํจ.
String classMember = (String) a.get(null); // ํด๋์ค ๋ฉค๋ฒ(static) name ๊ฐ์ ๊ฐ์ ธ์ด
String name = (String) a.get(new User("a","b","c")); // ํด๋น ์ธ์คํด์ค์ name ๊ฐ์ ๊ฐ์ ธ์ด
}
}
์ฐธ๊ณ ๋ก ๋ฉ์๋๋ ์๋์ ๊ฐ์ด ๊ฐ์ ธ์์ ์ฌ์ฉํ ์ ์๋ค.
Method m = User.class.getDeclaredMethod("methodName");
m.setAccessible(true); // ์ ๊ทผ์ ์ด์(private) ๋ฅผ ๋ฌด์ํจ.
m.invoke(๋ฉ์๋ ํ๋ผ๋ฉํ); // ๋ฉ์๋ ์คํ
@ ๋๋ง์ DI ์ปจํ ์ด๋ ๋ง๋ค์ด๋ณด๊ธฐ
์คํ๋ง ์ปจํ ์ด๋์์ getBean() ์ ์ด๋ป๊ฒ ๋์ํ๋ ๊ฑธ๊น? ๊ฐ๋จํ ๋๋ง์ ์ด๋ ธํ ์ด์ ์ปจํ ์ด๋๋ฅผ ๋ง๋ค์ด๋ณด์.
public Class BookService(){
@Inject // ์ง์ ๋ง๋ ์ด๋
ธํ
์ด์
BookRepository bookRepository;
}
public class Container {
private static <T> T createInstance(Class<T> classType) {
try {
// ๋งค๊ฐ๋ณ์๊ฐ ์๋ ๊ธฐ๋ณธ์์ฑ์๋ฅผ ์ฌ์ฉํ์ฌ ์ธ์คํด์ค ์์ฑ
return classType.getConstructor(null).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> T getObject(Class<T> classType) {
T myInstance = createInstance(classType); // 1. ๋ฆฌํ๋ ์
์ผ๋ก ํด๋น ๊ฐ์ฒด๋ฅผ ๋ง๋ฌ
Arrays.stream(classType.getDeclaredFields()).forEach(
field -> { // 2. ํ๋ ์ค์ Inject ์ด๋
ธํ
์ด์
์ด ์๋ค๋ฉด ๋์
if (field.getAnnotations(Inject.class) != null) {
// 3. ๋ฆฌํ๋ ์
์ผ๋ก ์ฃผ์
ํ ๊ฐ์ฒด์ ์ธ์คํด์ค๋ฅผ ๋ง๋ฌ.
Object fieldInstance = createInstance(field.getType());
field.setAccessible(true); // private ์ ๊ทผ์ ์ด์ ๋ฌด์
try {
// 4. @Inject๊ฐ ๋ถ์ ํ๋๋ฅผ ์ฐพ์, ๊ทธ ํ๋์ ์๋ง์ ๊ฐ์ฒด ๋ง๋ค์ด ์ฃผ์
ํจ.
field.set(myInstance, fieldInstance);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
);
// @Inject ๋ฅผ ์ด์ฉํ์ฌ ํ๋๊ฐ์ ์ํ๋ ๊ฐ์ฒด๋ฅผ ์ฃผ์
ํ [classType] ๊ฐ์ฒด๋ฅผ ๋ฐํํจ.
return myInstance;
}
}
์ด๋ ๊ฒ ์ด๋ ธํ ์ด์ ์ด ๋ฌ๋ฆฐ ๊ฐ์ฒด, ๋ฉ์๋๋ฅผ ์ฐพ์ ์ํ๋ ํ๋๋ฅผ ์ฝ์ ํ ์ ์๋ค. ์คํ๋ง์ ์ฑ๊ธํค ๋น๋ ์ด๋ฐ ์์ผ๋ก ๋ง๋ค์ด์ง๋ค. ์ด๋ ์๋์์ ํ๋ก์๋ฅผ ๋ฐฐ์ฐ๋ฉฐ ๋ ์์ธํ๊ฒ ์์๋ณด์.
@Bean
public MemberRepository memberRepository() {
if (memoryMemberRepository๊ฐ ์ด๋ฏธ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก๋์ด ์์ผ๋ฉด?) {
return ์คํ๋ง ์ปจํ
์ด๋์์ ์ฐพ์์ ๋ฐํ;
} else { //์คํ๋ง ์ปจํ
์ด๋์ ์์ผ๋ฉด
๊ธฐ์กด ๋ก์ง์ ํธ์ถํด์ MemoryMemberRepository๋ฅผ ์์ฑํ๊ณ ์คํ๋ง ์ปจํ
์ด๋์ ๋ฑ๋ก
return ๋ฐํ
}
}
@ ์ฃผ์์ฌํญ
๋ฆฌํ๋ ์ ์ ํ๋ ์์ํฌ๋ฅผ ํฌํจํด์ ๋ค์ํ ๊ณณ์์ ํ์ฉ๋๋ค.
- ์คํ๋ง - ์์กด์ฑ ์ฃผ์ , MVC ๋ทฐ์์ ๋์ค์จ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด์ ๋ฐ์ธ๋ฉํ ๋
- ํ์ด๋ฒ๋ค์ดํธ(JPA) - @Entity ํด๋์ค์ Setter๊ฐ ์๋ค๋ฉด ๋ฆฌํ๋ ์ ์ ์ฌ์ฉ
- JUnit - ์ด๋ ธํ ์ด์ ์ ์ด์ฉํ ํ ์คํธ ํ๊ฒฝ์ค์
๊ทธ๋งํผ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ด์ง๋ง, ๋ฆฌํ๋ ์ ์ ์๋ชป๋ ์ฌ์ฉ์ ์๋์ ๊ฐ์ ๋ฌธ์ ์ ์ ๋ง๋ค ์ ์๋ค๋ ๊ฑธ ๋ช ์ฌํ์.
- ๋ฆฌํ๋ ์ ์ ๊ณผ๋ํ ์ฌ์ฉ์ ์ฑ๋ฅ ์ด์๋ฅผ ๋ฐ์์ํค๊ธฐ ์ฝ๋ค. ๋ฆฌํ๋ ์ ์ด ๊ผญ ํ์ํ ๊ณณ์๋ง ์ฌ์ฉํ์.
- ๋ฆฌํ๋ ์ ์ ๋ฐํ์์์ ๊ฐ์ฒด๋ฅผ ์กฐ์ํ๋ ๊ธฐ์ ์ด๋ฏ๋ก ์ปดํ์ผ ํ์์ ํ์ธ๋์ง ์๋ ๋ฌธ์ ๋ฅผ ๋ง๋ค ์ ์๋ค.
- ๋ฐํ์์ ๊ฐ์ฒด๋ฅผ ์กฐ์ํ๊ธฐ์ ๋ง์๋ง ๋จน๋๋ค๋ฉด ์ ๊ทผ ์ง์์(private)๋ฅผ ๋ฌด์ํ ๊ฐ๋ฅ์ฑ์ด ์๋ค. (์บก์ํ ํ๊ดด)
# ๋ค์ด๋๋ฏน ํ๋ก์
๋ค์ด๋๋ฏน ํ๋ก์๋ ์ ํ๋ฆฌ์ผ์ด์ ์คํ๋์ค, ํน์ ์ธํฐํ์ด์ค๋ค์ [๊ตฌํํ๋ ํด๋์ค ๋๋ ์ธ์คํด์ค]๋ฅผ ์๋์ผ๋ก ๋ง๋๋ ๊ธฐ์ ์ด๋ค. ๋ค์ด๋๋ฏน ํ๋ก์๋ ๋ฆฌํ๋ ์ ๊ธฐ์ ์ ํตํด ๋ง๋ค์ด์ง๋ค.
- ์คํ๋ง ๋ฐ์ดํฐ JPA์์ ์ธํฐํ์ด์ค ํ์
์ ์ธ์คํด์ค๋ ๋๊ฐ ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ธ๊ฐ?
-> Spring AOP๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ฉฐ RepositoryFactorySupport์์ ํ๋ก์๋ฅผ ์์ฑํ๋ค.
-> Spring AOP์ ๊ฐ์ฅ ๋ฐ๋จ์์๋ Proxy ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
@Entity
public class Book{
...
}
@Service
public class BookService{
@Autowired // ์ธํฐํ์ด์ค์ ์๋์ฃผ์
BookRepository bookRepository;
}
public interface BookRepository extends JpaRepository<Book, Integer>{
}
// ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋ ๊ฒ ๊ฐ์ง๋ ์๊ณ , ์ธํฐํ์ด์ค์๋ ์๋ฌด๋ฐ ์ฝ๋๋ ์๋ค.
// ํ์ง๋ง ์์ @Autowired๋ ์ ์์ ์ผ๋ก ๋์ํ๊ณ , ์ค์ ์ธ์คํด์ค๊ฐ ์ฃผ์
๋๋ค.
// ์ด๋
ธํ
์ด์
๋ ์๋๋ฐ ์ด๋ป๊ฒ ๋ฆฌํ๋ ์
์ผ๋ก ์ด ๋ชจ๋ ๊ธฐ๋ฅ์ ๋ง๋ค ์ ์์์๊น?
@ ํ๋ก์ ํจํด
๋ค์ด๋๋ฏน ํ๋ก์๋ฅผ ์ดํด๋ณด๊ธฐ ์ด์ ์, 'ํ๋ก์'๊ฐ ์ ํํ๊ฒ ๋ฌด์์ธ์ง ์์๋ณด์.
Proxy๋ '๋๋ฆฌ์ธ' ์ด๋ผ๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๋ค. ๊ฐ๋ฐ์์ ์ฌ์ฉํ๋ Proxy ๊ฐ์ฒด๋ ํด๋ผ์ด์ธํธ-์๋ฒ ๋ฑ์ ๊ด๊ณ์์ ์ค๊ฐ์ ๋ผ์ด ๋๋ฆฌ์ธ ์ญํ ์ ํ๋ ๊ฐ์ฒด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
- ํ๋ก์์ ์ค์ ๊ฐ์ฒด๊ฐ ๊ณต์ ํ๋ ์ธํฐํ์ด์ค๊ฐ ์๊ณ , ํด๋ผ์ด์ธํธ๋ ์ธํฐํ์ด์ค๋ฅผ ํตํด ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ค.
- ํด๋ผ์ด์ธํธ๋ ์ค์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ๋์ํ์ง๋ง, ์ค์ ๋ก๋ ํ๋ก์๋ฅผ ํตํด์ ์ ๊ทผํ๊ฒ๋๋ค.
- ์ด๋ ๊ฒ ์ค๊ฐ์ ํ๋ก์๋ฅผ ์ฌ์ฉํจ์ผ๋ก์ ์ ๊ทผ๊ด๋ฆฌ(๋ณด์), ์บ์ฑ(์ฑ๋ฅ), ํํฐ๋ฑ์ ๋ถ๊ฐ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํ ์ ์๋ค.
- ๋ํ ์ค์ ๊ฐ์ฒด๋ ์์ ์ด ํด์ผํ ์ผ(๋จ์ผ์ฑ ์์์น, SRP)๋ง ์ง์คํ๋ฉด์ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ(๋ก๊น , ํธ๋์ญ์ ์ฒ๋ฆฌ๋ฑ)์ ํ๋ก์ ๊ฐ์ฒด์๊ฒ ๋๊ฒจ ๊ฐ์ฒด์งํฅ์ ์ธ ํ๋ก๊ทธ๋๋ฐ์ ํ ์ ์๊ฒ ๋๋ค.
public interface BookService{ ... }
// ํด๋ผ์ด์ธํธ๋ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค.
// ํ์ง๋ง ์ค์ ๋ก ํธ์ถ๋๋ ๊ฒ์ ์ค๊ฐ์ ์๋ ํ๋ก์ ๊ฐ์ฒด์ด๋ค.
public class BookServiceProxy implements BookService {
BookService bookService; // ์ค์ ๊ฐ์ฒด๋ฅผ ๋ด๋ ๊ณณ
@Override
public void rent(Book book){
bookService.rent(book); // ๋ณดํต์ ํ๋ก์๋ฅผ ํธ์ถํ๋ฉด ์ค์ ๊ฐ์ฒด์๊ฒ ์์ํ๋๋ก ์ค๊ณํ๋ค.
// ๋ฌผ๋ก ์ค์ ๊ฐ์ฒด๋ฅผ ํธ์ถํ์ง ์๊ณ ํ๋ก์์์ ์ํ๋ ๋์์ ํ๋๋ก ๋ฐ๊ฟ ์๋ ์๋ค.
}
}
@ ํ๋ก์์ ๋ฌธ์ ์
๊ธฐ์กด ํ๋ก์์ ๊ฐ์ฅ ํฐ ๋จ์ ์ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋ ๋ง๋ค ํ๋ก์๋ฅผ ์๋กญ๊ฒ ๋ง๋ค์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ํ๋ก์๋ฅผ ํ๋๋ง ์ฌ์ฉํ๋ค๋ฉด ๊ด์ฐฎ๊ฒ ์ง๋ง, ์ค์ ์ดํ๋ฆฌ์ผ์ด์ ์ ๊ตฌํํ ๋์๋ ์ ๋ง ๋ง์ ํ๋ก์๋ค์ด ์ค์ฒฉ๋๊ฒ ๋๋ค. ๊ทธ๋ ๋ค๊ณ ๋จ ํ๋์ ํ๋ก์์ ๋ชจ๋ ๊ธฐ๋ฅ์ ํฉ์น๊ฒ๋๋ฉด ํ๋ก์๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ ๊ฐ์ฒด์ ๋ถ๋ฆฌํ๋ ์๋ฏธ ์์ฒด๊ฐ ์์ด์ง๊ฒ ๋๋ค. ๊ทธ๋ฅ ๊ฐ์ฒด๊ฐ ๋ง์์ ธ ์ฝ๋ ๊ด๋ฆฌ๋ง ๋ณต์กํด์ง๋ฟ์ด๋ค.
- ์ค์ ๊ฐ์ฒด๊ฐ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค๋ฉด ํ๋ก์ ๋ํ ์ฌ์ฉํ์ง๋ ์๋ ๋ชจ๋ ๋ฉ์๋๋ฅผ @Override ํด์ผํ๋ค.
- ํ๋ก์๊ฐ ์ค์ฒฉ๋๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ , ๊ฐ์ ์ฝ๋์ ์ค๋ณต์ด ๋ฐ์ํ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ์ํด ๋์จ ๊ฒ์ด ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํด์ ๋ฐํ์์ ๋ง๋๋ ๋์ ํ๋ก์, ์ฆ Dynamic proxy์ด๋ค. ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ์์๋ฐ์ ํ๋ํ๋ ์ง์ ๋ง๋๋๊ฒ ์๋๋ผ, ๋ฐํ์ ์์ ์ [ํด๋์ค ์ ๋ณด Object.class]๋ฅผ ์ด์ฉํ์ฌ ์ด๋ ธํ ์ด์ , ๋ฉ์๋์ ๋ฐ๋ผ ๋์ ์ผ๋ก ๋ค๋ฅธ ๋ฉ์๋ ๋์์ ์คํํ๋๋ก ํ๋ก์๋ฅผ ๋ง๋ค ์ ์๋ค.
@ ๋ค์ด๋๋ฏน ํ๋ก์
๋ค์ด๋๋ฏน ํ๋ก์ : ๋ฐํ์์ ํน์ ์ธํฐํ์ด์ค๋ค์ ๊ตฌํํ๋ ํด๋์ค ๋๋ ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๊ธฐ์ , ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํด ์ง์ ๊ตฌํํด๋ ์๊ด์์ง๋ง, ํธํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด ์๋ฐ ๋ฆฌํ๋ ์ API ์์ ๋ค์ด๋๋ฏน ํ๋ก์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
์กฐ๊ธ ์ฝ๊ฒ ๋งํ์๋ฉด,
- ์ค์ ๊ฐ์ฒด์ ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋ ๋ค.
์๋ฐ API๋ก๋ ์ค์ ๊ฐ์ฒด๋ฅผ ๋ฐ๋ก ์์๋ฐ์์ ์กฐ์ํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์๋๋ค. - ํ๋ก์ ๊ฐ์ฒด์์ ํด๋น ๊ฐ์ฒด์ ๋ฆฌํ๋ ์ Class<T> (myClass.class)๋ฅผ ์กฐ์ํ๊ณ ์กฐ์๋ Class<T>๋ฅผ ์ด์ฉํด์ ๋ฐํ์์ [ํ๋ก์ ๊ฐ์ฒด ์ธ์คํด์ค]๋ฅผ ๋ง๋ ๋ค.
- ๊ฐ๋ฐ์๋ ๋ฆฌํ๋ ์ Class<T>๋ฅผ ์ฝ๊ฒ ์กฐ์ํ๊ธฐ ์ํด์ Java API์ ์๋ InvocationHandler ๊ฐ์ฒด์ invoke() ๋ฉ์๋๋ง ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ์กฐ์ํ๊ณ ์ถ์ ๋ด์ฉ์ ์์ฑํ๋ฉด ๋๋ค.
- ์ด๋ ๊ฒ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ฉด, ํน์ ๋ฉ์๋๋ @์ด๋ ธํ ์ด์ ๊ฐ์ ๋ฐ๋ผ ๋์ ์ผ๋ก ๋์์ด ๋ฌ๋ผ์ง๋ ํ๋ก์๋ฅผ ๋ง๋ค ์ ์๋ค. => ๋ค์ด๋๋ฏน ํ๋ก์
public class BookServiceProxy implements BookService {
// java.lang.reflect.Proxy.newProxyInstance
// BookService ์ธํฐํ์ด์ค์ ํ๋ก์ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํ์์ ๋์ ์ผ๋ก ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ.
BookService bookService = (BookService) Proxy.newProxyInstance(
BookService.class.getClassLoader() // ํ๋ก์๋ฅผ ๋ง๋ค ClassLoader
,new Class[]{BookService.class} // ํ๋ก์๋ฅผ ๋ง๋ค ์ธํฐํ์ด์ค
,new InvocationHandler() { //invoke handler, ํ๋ก์์ ๋ด์ฉ. ์ง์ ์์ฑํ ๊ตฌํ์ฒด
BookService bookService = new DefaultBookService();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ์๋ฌด๋ฐ ๋์๋ ํ์ง์๊ณ , ๋จ์ํ ์ค์ ๊ฐ์ฒด์๊ฒ ์์ํ๋ ํ๋ก์ ์ฝ๋.
// Object invoke = method.invoke(...)๋ฑ์ผ๋ก ๋ฆฌํ๋ ์
์กฐ์๊ฐ๋ฅ.
// method.invoke(๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฐ์ฒด , ๋ฉ์๋์ ์ ๋ฌํ ํ๋ผ๋ฉํ)
// ์ฌ๊ธฐ์ ์ถ๊ฐ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๊ฐ์ฒด์ ๋ชจ๋ ๋ฉ์๋๊ฐ ํด๋น ์ฝ๋๋ฅผ ๋์ํ๋ค.
return method.invoke(bookService, args);
}
});
/*... ๊ธฐํ ๋ฉ์๋ ์๋ต ...*/
}
๋ง์ฝ ํน์ ๋ฉ์๋( rent() )๋ง ์ถ๊ฐ์ ์ธ ๋์์ ํ๋๋ก ๋ง๋ค๊ณ ์ถ๋ค๋ฉด, ์๋์ ๊ฐ์ด ์กฐ๊ฑด์ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค.
if (method.getName().equals("rent")) {
System.out.println("aaaa");
Object invoke = method.invoke(bookService, args);
System.out.println("bbbb");
return invoke; // ์ด์ bookService.rent()๋ ์คํ ์ ํ๋ก aaaa, bbbb๋ฅผ ์ถ๋ ฅํ๋ค.
}
๋ค๋ง ์๋ฐ API์์ ์ ๊ณตํ๋ ๋ค์ด๋๋ฏน ํ๋ก์๋ ์ธํฐํ์ด์ค์์๋ง ์์ฑํ ์ ์๋ค.(BookService.class.getClassLoader()). ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋๊ฒ ์ฝ๋๋ ๊น๋ํ๊ณ ์ ์ฝ์ฌํญ๋ ์๋ค. + ์๋ง ์๋ฐ ์ฝ๋์ ํ์ ํธ์์ฑ ๋๋ฌธ์ ์ ๊ณตํ์ง ์๋๊ฒ ์๋๊ฐ ์ถ๋ค. ์ค์ ๋ก ํด๋์ค๋ก ์์ฑํ๋ ค๊ณ ํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋์ค๊ฒ ๋๋ค. ๋ง์ฝ ํด๋์ค์ ๋ค์ด๋๋ฏน ํ๋ก์๊ฐ ํ์ํ๋ค๋ฉด ๋ค๋ฅธ ๋ฐฉ๋ฒ์ด ์๋๋ฐ, ์ด๋ ์๋์ [@ํด๋์ค์ ํ๋ก์ ๋ง๋ค๊ธฐ] ์์ ์์๋ณด๋๋ก ํ์.
- JDK dynamic proxy๋ก ๊ตฌํ๋๋ฉด ํ๋ก์ ์ฌ์ฉ์ ์ํด Interface ์ฌ์ฉ์ด ๊ฐ์ ํ ํ๋ค๋ ๋จ์ ์ด ์๋ค.
- Dynamic Proxy๋ Invocation Handler๋ฅผ ์์๋ฐ์์ ์ค์ฒด๋ฅผ ๊ตฌํํ๊ฒ ๋๋๋ฐ, ์ด ๊ณผ์ ์์ ํน์ ๊ฐ์ฒด๋ฅผ ํต์ผ๋ก Reflection์ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ผ์ ์ฑ๋ฅ์ด ์กฐ๊ธ ๋จ์ด์ง๋ค.
@ ์คํ๋ง AOP์ ํ๋ก์
ํ์ง๋ง ์์ ๊ฐ์ด ์ง์ ๋์ ํ๋ก์๋ฅผ ๋ง๋๋ ๊ฒ์ ์ฝ๋๋ ์๋นํ ๋ณต์กํด์ง๊ณ ๊ตฌํํ๊ธฐ๋ ์ด๋ ต๋ค. ๊ทธ๋์ ์คํ๋ง์์๋ ๋ฆฌํ๋ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(CGlib)๋ฅผ ์ด์ฉํ์ฌ ์์ ๊ฐ์ ๋ณต์กํ ๊ตฌ์กฐ๋ฅผ ์ถ์ํ๋ ์ธํฐํ์ด์ค๋ก ํฌ์ฅํด์ ์ ๊ณตํด์ฃผ๋๋ฐ, ์ด๊ฒ์ด ๋ฐ๋ก ์คํ๋ง AOP์ด๋ค.
- ๊ทธ๋์ ์คํ๋ง AOP๋ ํ๋ก์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ค๊ณ ๋งํ๋ค. ์ด๋ ์คํ๋ง์ ๊ณต๋ถํ๋ฉด์ ๋ฐฐ์๋ณด๋๋ก ํ์.
- ์คํ๋ง์์ AOP๋ฅผ ์ด๋ป๊ฒ ๋ง๋ค์๋๊ฐ ๊ถ๊ธํ๋ฉด [ํ ๋น์ ์คํ๋ง3.1, 6์ฅ-AOP]๋ฅผ ์ฝ์ด๋ณด๋๋ก ํ์.
@ ํด๋์ค์ ํ๋ก์ (์ธํฐํ์ด์ค๊ฐ ์๋ ๊ฒฝ์ฐ, ์์ ํ๋ก์)
์์์ ๋งํ์ง๋ง ์๋ฐ API์์ ์ ๊ณตํ๋ ๋ค์ด๋๋ฏน ํ๋ก์๋ ์ธํฐํ์ด์ค์์๋ง ์์ฑํ ์ ์๋ค. ํด๋์ค ํ๋ก์๊ฐ ํ์ํ๋ค๋ฉด ์ง์ ๋ฆฌํ๋ ์ ์ผ๋ก ๊ตฌํํ๊ฑฐ๋ ์๋์ ๊ฐ์ ๋ฆฌํ๋ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ฌ์ฉํ๋ฉด ๋๋ค.
์ฐธ๊ณ ๋ก ํ๊ฒ ๊ฐ์ฒด์ ์์์ ์ด์ฉํด์ ํ๋ก์๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ Subclass ํ๋ก์๋ผ๊ณ ๋ ํ๋ค.
- CGlib ( https://github.com/cglib/cglib/wiki )
- CGlib๋ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ์ฌ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํด์ฃผ๋ ์ฝ๋ ์์ฑ(Code gen)๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
- ๋ฐํ์์ ๋์ ์ผ๋ก ์๋ฐ ํด๋์ค์ ํ๋ก์ ์์ฑ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค
- ์คํ๋ง, ํ์ด๋ฒ๋ค์ดํธ๊ฐ ์ฌ์ฉํ๋ ๋ฆฌํ๋ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (๋ฌผ๋ก Java ๋ฆฌํ๋ ์ API๋ ๊ฐ์ด ์ฌ์ฉํ๋ค)
- ๋ฒ์ ํธํ์ฑ์ด ์ข์ง ์์์ ์๋ก ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด๋ถ์ ๋ด์ฅ๋ ํํ๋ก ์ ๊ณตํ๊ธฐ๋ ํ๋ค.
- ์คํ๋ง์์๋ ๊ฐ์ฒด๋ฅผ ์์ํด์ ํ๋ก์๋ฅผ ๋ง๋ค๊ณ CGlib๋ฅผ ์ด์ฉํด ๋ฆฌํ๋ ์ ์ ํ์ฉํ๋ค.
์ ์คํ๋ง ๋น ๊ฐ์ฒด์ ์์ฑ์๋ protected, public ๋ฐ์ ์ฌ์ฉํ ์ ์๋๊ฐ? ์ ๋ํ ๋ต๋ณ์ด ๋๋ค. - JDK API์์ ์ ๊ณตํ๋ ๋์ ํ๋ก์์์ ์ฐจ์ด์
- JDK ํ๋ก์๋ ์ธํฐํ์ด์ค์ ์ ํ์๋ ๋ชจ๋ ๋ฉ์๋์ ํ๋ก์๋ฅผ ์ ์ฉ์ํจ๋ค. ๊ทธ๋ฆฌ๊ณ Class<T>์ ๋ณด๋ก ์กฐ๊ฑด๋ฌธ์ ๋ง๋ค์ด ํน์ ๋ฉ์๋๋ง ์ถ๊ฐ๋์์ ํ๋๋ก ๋ง๋ค ์ ์๋ค.
- CGlib๋ MethodInterceptor ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ๋ฉ์๋๋ง ํ๋ก์ํ ํ๊ณ , ๋๋จธ์ง๋ ํ๋ก์๋ฅผ ๊ฑฐ์น์ง ์๊ณ ์ค์ ๊ฐ์ฒด ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ๋ง๋ค ์ ์๋ค.
- ์ด๊ฒ ๊ฐ๋ฅํ ์ด์ ๋ CGlib๋ ์ธํฐํ์ด์ค๊ฐ ์๋ ํ๊ฒ ๊ฐ์ฒด๋ฅผ ์ง์ ์์๋ฐ์ ๋ง๋ค๊ธฐ ๋๋ฌธ์ด๋ค.
MethodInterceptor handler = new MethodInterceptor() {
private final MethodMatcher methodMatcher; // ํน์ ๋ฉ์๋๋ฅผ ์ฐพ๋ ๊ฐ์ฒด
BookService bookService = new BookService();
public MethodInterceptor(MemthodMatch m){
this.methodMatcher = m;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy
methodProxy) throws Throwable {
if (methodMatcher.matches(method)) {
// CGlib๋ ์์๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ธฐ์ ํน์ ๋ฉ์๋๋ง ํ๋ก์๋ก ๋ฐ๊ฟ ์ ์์
// JDK ๋ค์ด๋๋ฏน ํ๋ก์์ ๋ค๋ฅด๊ฒ ํ์์๋ ๋ฉ์๋๋ ํ๋ก์๋ฅผ ์๊ฑฐ์น๊ฒ ํ ์์์.
return methodName.toUpperCase();
}
return method.invoke(bookService, objects);
}
};
// CGlib๋ก ์กฐ์ํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฝ์
BookService bookService = (BookService) Enhancer.create(BookService.class, handler);
- ByteBuddy
- ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์์ ์ ๊ณตํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ง๋ง Cglib์ฒ๋ผ ๋ฐํ์(๋ค์ด๋๋ฏน) ํ๋ก์๋ฅผ ๋ง๋ค ๋๋ ์ฌ์ฉํ๋ค.
- CGlib์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์์ ์ด์ฉํ (subClass) ํ๋ก์๋ฅผ ์ง์ํ๋ค.
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("Hello World!"))
.make()
.load(getClass().getClassLoader())
.getLoaded();
assertThat(dynamicType.newInstance().toString(), is("Hello World!"));
* ๋ค๋ง ๋ฆฌํ๋ ์ ๊ณผ ํ๋ก์๋ฅผ ์ฌ์ฉํ ์ ๋์ ์ดํ๋ฆฌ์ผ์ด์ ์ด๋ผ๋ฉด ๋น์ฆ๋์ค ๊ท๋ชจ๊ฐ ํฌ๊ณ ์ฝ๋๊ฐ ๋ณต์กํ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ๊ทธ๋์ ๊ฐ๋ฅํ๋ฉด ํด๋์ค๋ฅผ ์์๋ฐ์์ ๋ง๋๋ ๊ฒ ๋ณด๋ค๋, ๊ฐ๋ฅํ๋ฉด ์ธํฐํ์ด์ค ํ๋ก์๋ฅผ ์ฌ์ฉํด์ ๋ง๋๋ ๊ฒ์ด ์ข๋ค.
- ์์์ ์ฌ์ฉํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ ํ๋ก์๋ฅผ ๋ง๋ค ์ ์๋ค. (์คํ๋ง ๋น private, final ํด๋์ค๊ฐ ์๋๋ ์ด์ )
- ํด๋์ค๋ก ํ๋ก์๋ฅผ ๊ณ์ ์์๋ฐ์ผ๋ฉด ๋น์ฆ๋์ค๊ฐ ์ปค์ก์ ๋ ์ ์ง๋ณด์, ํ์ฅ์ด ํ๋ ์ฝ๋๊ฐ ๋ ์ ์๋ค.
- ์ฆ ๊ฐ๋ฅํ๋ฉด ํด๋์ค๊ฐ ์๋ ์ธํฐํ์ด์ค์ ํ๋ก์๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ ์ฐํ ์ฝ๋๋ฅผ ๋ง๋ค๊ธฐ ์ข๋ค.
@ ๋ค์ด๋๋ฏน ํ๋ก์ ์ ๋ฆฌ (๋ฐ์ดํธ์ฝ๋ Weaving)
๋ค์ด๋๋ฏน ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ค๋ ๋ง์ ๋ฐํ์์ [์ธํฐํ์ด์ค ํ๋ก์ ์ธ์คํด์ค] ๋๋ [ํด๋์ค์ ํ๋ก์ ์ธ์คํด์ค] ๋๋ [๋ฆฌํ๋ ์ ์ ์ด์ฉํ ํด๋์ค ์์ฒด]๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์ฌ์ฉํ๋ค๋ ๋ง์ด๋ค.
์ด๋ ๊ฒ ์ํ๋ ํ๊ฒ ๊ฐ์ฒด์ ํ๋ก์๋ฅผ ์ ์ฉ์์ผ, ๋์ ์ผ๋ก [ํ๋ก์๋ ๊ฐ์ฒด]๋ฅผ ์ฝ์ ํ๋ ๊ธฐ์ ์ Runtime Weaving์ด๋ผ๊ณ ํํํ๋ค. ์คํ๋ง์์๋ JDK Weaving, CGlib Weaving์ ์ฌ์ฉํ๋๋ฐ ์ด๋ ์์์ ์ค๋ช ํ ๋ฆฌํ๋ ์ ,ํ๋ก์ ๋ด์ฉ๊ณผ ๊ฐ์ ๋ง์ด๋ค.
๋ค์ด๋๋ฏน ํ๋ก์๋ ์๋์ ๊ฐ์ด ๋ค์ํ ๊ณณ์ ์ฌ์ฉ๋๋ค.
- ์คํ๋ง AOP
AOP๋ ์ค๋ช ํ๋ฉด ๋๋ ์์ผ๋ฏ๋ก, ๊ถ๊ธํ๋ค๋ฉด [ํ ๋น์์คํ๋ง3.1v 6์ฅ AOP]๋ฅผ ์ฝ์ด๋ณด์.
- ํ์ด๋ฒ๋ค์ดํธ Lazy Initialization
@Entity
@Getter
public class Book {
@Id @GeneratedValue
private Integer id;
private String title;
@OneToMany
private List<Note> notes;
// Book์ ์กฐํํ ๋ List<Note>๋ฅผ ๊ฐ์ด ๊ฐ์ ธ์ค์ง ์๋๋ค. ์ค์ notes๋ฅผ ์ฌ์ฉํ ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ฐ์์์ผ ๋ฐ์์จ๋ค.
// ์ด๋ Fetch ์ ๋ต์ Lazy๋ก ํ๊ธฐ ๋๋ฌธ. ๊ทธ๋ ๋ค๋ฉด notes๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ๊น์ง๋ ๊ฐ์ null ์ธ๊ฐ?
// ํ์ง๋ง ์ค์ ๋ก ์กฐํํด๋ณด๋ฉด null์ด ์๋๋ค. ๊ทธ ์ด์ ๋ ๋ค์ด๋๋ฏน ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค.
}
- Mockito (์ง์ง์ฒ๋ผ ๋์ํ๋ ํ ์คํธ์ฉ Mock ๊ฐ์ฒด๋ฅผ ์ง์ํ๋ ํ๋ ์์ํฌ)
import static org.mockito.Mockito.mock;
// ์ค์ ๊ฐ์ฒด๋ฅผ ์์ํ ๋ค์ด๋๋ฏน ํ๋ก์๋ฅผ ํ์ฉํด์ ๋ง๋ mock ๊ฐ์ฒด.
BookRepository bookRepositoryMock = mock(BookRepository.class);
// Mockito ํ๋ ์์ค์ ์ด๋ ๊ฒ ํน์ ๋ฉ์๋์ ๋ฐํ๊ฐ์ ๋ด๊ฐ ์ํ๋ ๊ฐ์ฒด๋ก ์กฐ์ ๊ฐ๋ฅ (mocking)
Book testBook = new Book("myBookName");
when(bookRepositoryMock.save(any())).thenReturn(testBook); // ์๋ฌด๊ฑฐ๋ ๋ฃ์ด๋ testBook ๋ฐํ
BookService bookService = new BookService(bookRepositoryMock);
bookService.rent(new Book("custom name"))
book.rent() // Mockito์ ๋ค์ด๋๋ฏน ํ๋ก์ ์ ์ฉ => ๋ฌด์กฐ๊ฑด Book("myBookName")์ด ๋ฐํ๋จ
public class BookService {
BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public void rent(Book book) {
Book savedBook = bookRepository.save(book);
System.out.println("rent: " + savedBook.getTitle());\
}
}
# ์ ๋ ธํ ์ด์ ํ๋ก์ธ์
์! ๊ทธ๋ ๋ค๋ฉด Lombok๋ ๋ค์ด๋๋ฏน ํ๋ก์์ฒ๋ผ ๋ฆฌํ๋ ์ ์ ํ์ฉํด์ ๋์ํ๊ฒ ๊ตฌ๋! ํ๊ณ ์๊ฐํ ์ ์์ง๋ง, ์ด๋ ๋ฆฌํ๋ ์ ์ ์ด์ฉํด์ ๊ตฌํํ ๊ฒ์ด ์๋๋ค. ์ด์ ๋ํด์ ์์๋ณด๋๋ก ํ์.
@ JDK 1.5, ์ ๋ ธํ ์ด์ ์ ๋ฑ์ฅ
์ ๋ ธํ ์ด์ ์ ์์ค์ฝ๋์ ์ปดํ์ผ ํ์์ ์ฝ์ ์ ์๋ ์ฃผ์์ ํตํด ๋ฉํ๋ฐ์ดํฐ(์ค์ ์ ๋ณด)๋ฅผ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ด๋ค.
- ๊ธฐ์กด์ ์๋ฐ ํ๋ก๊ทธ๋จ์ ์์ค์ฝ๋์ ๊ทธ์ ๋ํ ๋ฌธ์๋ฅผ ๋ฐ๋ก ์์ฑํ๋๊ฒ ์ผ๋ฐ์ ์ด์๋ค. ํ์ง๋ง ๋งค๋ฒ ์ฝ๋์ ๋ฌธ์๋ฅผ ๋๊ธฐํํด์ฃผ๋ ์์ ์ ๋งค์ฐ ๋ฒ๊ฑฐ๋ก์ ๊ณ , ์๋ฐ ๊ฐ๋ฐ์๋ค์ ์์ค์ฝ๋์ ์ฃผ์์ผ๋ก ๋ถํฐ HTML๋ฌธ์๋ฅผ ์๋ ์์ฑํด๋ด๋ javadoc.exe๋ฅผ ๋ง๋ค๊ฒ ๋์๋ค.
- ๊ธฐ์กด์ ์๋ฐ ์ค์ ํ์ผ์ ํ๋ก์ ํธ ๋ด์ XML ํ์ผ์ ํํ๋ก ๊ด๋ฆฌํ์๋๋ฐ, ์ด๊ฒ๋ ๋ฌธ์์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ค์ฝ๋์ ํจ๊ป ๊ด๋ฆฌํ ์ ์์๊น? ๋ผ๋ ์์ด๋์ด์์ ๋์ค๊ฒ ๋ ๊ฒ์ด ์ปดํ์ผ๋ฌ๋ฅผ ์ํ ์ฃผ์, [@์ ๋ ธํ ์ด์ ] ์ด๋ค. ์ ๋ ธํฐ์์ ์ ๋ฑ์ฅ์ผ๋ก ์ค์ ํ์ผ์ ์๋ฐ์ฝ๋ ๋ด์ ์ฝ์ ํ๊ณ ๊ฐ๋จํ๊ฒ ์ค์ ์ ๋ณ๊ฒฝํ ์ ์๊ฒ ๋ฐ๋์๋ค.
ํ์ฌ ์๋ฐ์์ ์ด๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ 3๊ฐ์ง๊ฐ ์๋ค.
- ์ปดํ์ผ๋ฌ์๊ฒ ๋ฌธ๋ฒ ์๋ฌ๋ฅผ ํ์คํ๊ฒ ์ฒดํฌํ๋๋ก ์ ๋ณด ์ ๊ณต (@Override๋ฑ)
- ์ปดํ์ผ ํ์์ ์๋์ผ๋ก ์ฝ๋๋ฅผ ์์ฐํ ์ ์๋ ์ ๋ณด ์ ๊ณต (์ ๋ ธํ ์ด์ ํ๋ก์ธ์)
- ๋ฐํ์์ ๋์ ์ผ๋ก ํน์ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋๋ก ์ ๋ณด๋ฅผ ์ ๊ณต (๋ฆฌํ๋ ์ )
@ Java(JVM)์ ๋ค์ํ ๊ธฐ๋ฅ
์ปดํ์ผ๋ ๋ฐ์ดํธ์ฝ๋(.class)๋ JVM์ด ์ฝ์ด๋ค์ฌ ํ๋ก๊ทธ๋จ์ ์คํํ๋ค. JVM์์๋ ํน์ ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์์ ๋ ๋ค๋ฅธ ๋์์ ํ๋๋ก ์ปดํ์ผํด์ฃผ๊ฑฐ๋ ์์ ๋ฐํ์ ์์ ์์ ํน์ ๋์์ด ๋ฐ์ํ์์ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ ๋ค๋ฅธ ์ฝ๋๋ฅผ ์คํํ๊ฒ ๋ง๋๋ hook๋ฑ์ ์ ๊ณตํ๋ค.
- Shutdown Hook
- JDK1.3์ ๋์จ ๊ธฐ๋ฅ์ผ๋ก JVM์ด ์ข ๋ฃ๋ ๋ ์์ ์ ๊ฐ๋ก์ฑ์ ํน์ ์ฝ๋๋ฅผ ์คํํ๋๋ก ๋ง๋ค ์ ์๋ค. - Annotation Processor
- JDK1.5~1.6์ ๋์จ ๊ธฐ๋ฅ์ผ๋ก ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ ๋น๋ ํด์ด๋ค.(javac.exe, ์ฆ ์๋ฐ ์ปดํ์ผ๋ฌ์ ํฌํจ๋์ด์์)
- ์ปดํ์ผ ๋์ค ์ ๋ ธํ ์ด์ ์ ๋ง๋๋ฉด ํน์ ํ ๋์์ ํ๋๋ก ๋ง๋ค ์ ์๋ค.
- AbstractProcessor ๊ฐ์ฒด๋ฅผ ์์๋ฐ์ ์ฌ์ฉํ ์ ์๋ค. - ๊ฐ์ฒด ์ง๋ ฌํ
- JDK1.1์ ๋์จ ๊ธฐ๋ฅ์ผ๋ก Serializable ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ผ๋ฉด JVM์ด ์๋์ผ๋ก ํด๋น ๊ฐ์ฒด๊ฐ ์ง๋ ฌํ(๊ฐ์ฒด -> ๋ฐ์ดํธ์ฝ๋ ์ ํ)๋ ์ ์๋๋ก ๋ง๋ค์ด์ค๋ค.
@ Lombok์ด๋
https://projectlombok.org/features/all
์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ์ผ๋ก ์์ฃผ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ๋์ ์์ฑํด์ฃผ๋ ์๋ฐ ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ํ์ฌ ์๋ฐ์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, @Getter @Setter @Builder๋ฑ์ ์ ์ฉ ์ด๋ ธํ ์ด์ ๊ณผ ์ด๋ ธํ ์ด์ ํ๋ก์ธ์ ์ฝ๋(AbstractProcessor)๋ฅผ ์ ๊ณตํ๋ค.
@ Lombok์ ๋์์๋ฆฌ
- ์ฐ์ lombok ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๋ฉด ์ ์ฉ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ ์๋ค.
- [์ ๋ ธํ ์ด์ ํ๋ก์ธ์]๋ฅผ ์ด์ฉํด ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ๋ด๋ถ์์ AbstractProcessor๋ฅผ ์์๋ฐ์ ์ฌ์ฉํ๋ค.
- ์ ๋
ธํ
์ด์
๋ค๋ก ์ ์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์ปดํ์ผ ์์ ์ [์ ๋
ธํ
์ด์
ํ๋ก์ธ์]๋ฅผ ์ด์ฉํ์ฌ ์์ค์ฝ๋์ AST(abstract syntax tree)๋ฅผ ์กฐ์ํ๋ค. ์ด๋ฅผ ์ด์ฉํด์ ๋ง์น ์ฝ๋๊ฐ ๋ฐ๋ ๊ฒ์ฒ๋ผ ๋์ํ๊ฒ ๋ง๋ ๋ค.
- ๋ค๋ง ์ด๋ ์๋ฐ์์ ๊ณต์์ ์ผ๋ก ์ ๊ณตํ๋ ์ฐธ์กฐ๋ง ๊ฐ๋ฅํ ๊ฐ์ฒด(TypeElement ์ RoundEnvironment)๋ฅผ ํ์ํ์ ์ผ๋ก ๊ฐ์ ์บ์คํ ํ์ฌ javac๊ฐ ์ฌ์ฉํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ AST๋ฅผ ์กฐ์ํ๋ค. ์ผ์ข ์ ์ปดํ์ผ๋ฌ ํดํน์ด๋ผ๊ณ ๋ณผ ์๋ ์๋ค.
- ๊ทธ๋์ ์ดํด๋ฆฝ์ค๋ agent๋ก, VsCode์ IntelliJ๋ ํ๋ฌ๊ทธ์ธ์ผ๋ก lombok์ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ณตํด์ค๋ค.
(IntelliJ์์๋ ํ๋ฌ๊ทธ์ธ์ ์ค์นํ๊ณ , Lombok ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ importํ ํ ์ด๋ ธํ ์ด์ ํ๋ก์ธ์ค๋ฅผ ํ์ฑํ ์ํค๋ฉด ์ฌ์ฉํ ์ ์๋ค.)
๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ Lombok์ ์ฌ์ฉํ๋ ์ด์ , ๋ ผ๋๊ฑฐ๋ฆฌ
Lombok์ ๊ณต๊ฐ๋ API๊ฐ ์๋ ์ปดํ์ผ๋ฌ ๋ด๋ถ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ์์ค์ฝ๋๋ฅผ ์กฐ์ํ๋ค.
์ฌ์ง์ด ์ดํด๋ฆฝ์ค์ Lombok์ java agent๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํ์ผ๋ฌ ๋ด๋ถ ํด๋์ค ์์ฒด๋ ์กฐ์ํด๋ฒ๋ฆฐ๋ค. ํด๋น ํด๋์ค๋ค ์ญ์ ๊ณต์์ ์ผ๋ก ๊ณต๊ฐ๋ API๊ฐ ์๋๋ค๋ณด๋, JVM์ด ์ ๋ฐ์ดํธ ๋๋ฉด์ ๋ฒ์ ํธํ์ฑ๋ฑ ์ ๋ง ๋ค์ํ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ ์ํ์ฑ์ ๊ฐ์ง๋ค.
๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ lombok์ด ์ ๊ณตํ๋ ์์ฒญ๋ ํธ์๊ธฐ๋ฅ๋ค ๋๋ถ์ ์๋์ ์ธ ์ฌ์ฉ๋ฅ ์ ๋ณด์ด๊ณ ์๋ค. ๋ฌผ๋ก ์๋ฐ ๊ณต์API๋ง์ ์ฌ์ฉํ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค๋ ์์ง๋ง, lombok๋งํผ ํธํ์ง๋ ์์์ ๋์ฒดํ์ง ๋ชปํ๋ ํ์ค์ด๋ค.
- ๊ตฌ๊ธ์ AutoValue (https://github.com/google/auto/blob/master/value/userguide/index.md)
- Immutables (https://immutables.github.io/)
์ฌ์ค ์ด๋ฌํ ๋ ผ๋์ ๋กฌ๋ณต ๊ณต์ํํ์ด์ง์ Lombok์ ๋ง๋ ๊ฐ๋ฐ์์ ๋ธ๋ก๊ทธ์๋ ์ ์์ ์ธ ์๋ฐAPI ์ฌ์ฉ์ด ์๋ ํดํน์์ ์ธ์ ํ๊ณ ์๊ณ , ๊ฐ๋ฐ์์ ์ ์ฅ์ด ์์ธํ๊ฒ ์ ํ์๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๋ ์ ๋ ธํ ์ด์ ํ๋ก์ธ์์ ์๋ฆฌ๋ฅผ ์ดํดํ๋ ค๊ณ ํ๋๊ฑฐ์ง Lombok์ ๋ํด ์๊ณ ์ํ๋ ๊ฒ์ด ์๋๊ธฐ์, ์ด์ ๋๋ง ์๊ณ ๋์ด๊ฐ๋๋ก ํ์.
# ์ ๋ ธํ ์ด์ ํ๋ก์ธ์ ๊ตฌํํด๋ณด๊ธฐ
@ 1. ์ฌ์ฉํ ์ด๋ ธํ ์ด์ ์ ์ ์ํ๋ค.
@Target(ElementType.TYPE) //ํ์
์ผ๋ก ์ง์ ํ๋ฉด ์ธํฐํ์ด์ค, ํด๋์ค, enum์ ์ฌ์ฉ๊ฐ๋ฅํ๋ค.
@Retention(RetentionPolicy.SOURCE) // ์ปดํ์ผ ์์ ์๋ง ์กด์ฌํ๋ ์ด๋
ธํ
์ด์
์ ๋ณด์ด๋ค.
public @interface Magic {
}
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface MyCustomAnnotation {
String tag();
}
@ 2. AbstractProcessor๋ฅผ ์์๋ฐ์ ์ด๋ ธํ ์ด์ Parser๋ฅผ ๋ง๋ ๋ค.
- ์ด๋ ธํ ์ด์ ํ๋ก์ธ์๋ฅผ ์ค์ ์์ด ์ปดํ์ผ๋ฌ์ ๋ฑ๋กํ ์ ์๊ฒ ๊ตฌ๊ธ์ auto-service(@AutoService)๋ฅผ ์ฌ์ฉํ๋ค.
- ์ด๋ฅผ ๋ค๋ฅธ๋ง๋ก Service Provider ๋ ์ง์คํธ๋ฆฌ(์ค์ ์ ๋ณด) ์์ฑ๊ธฐ๋ผ๊ณ ํ๋ค. Service Provider๋ฅผ ์ฌ์ฉํ๊ธฐ์ํ
[ META-INF/services/javax.annotation.processor.Processor ] ์ ๋ณด๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ค. - ServiceProvider๋ JDK1.6๋ถํฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ผ๋ก Java์ META-INF ๋๋ ํ ๋ฆฌ์ ์ธํฐํ์ด์ค๋ ํด๋์ค ์ ๋ณด๋ฅผ ์์ฑํ๋ฉด ๊ฐ๋ฐ์๊ฐ ๋ฐ๋ก ๊ฐ์ฒด๋ฅผ ์ ์ธ, ์์ฑํ์ง ์์๋ ํด๋น ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์๊ฒ ๋ง๋ค์ด์ค๋ค. (์ค์ ์์ญ์ ๋ถ๋ฆฌ)
@AutoService(Processor.class)
public class AutoFactoryProcesser extends AbstractProcessor {
@Override
// initํจ์๋ก processingEnvironment ๊ฐ์ฒด๋ฅผ ์ป์ ์ ์๋ค.
// ์ด ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ ํ์ผ์์ฑ(Filer)๊ณผ ๋ก๊น
(Message)์ ํ ์ ์๋ค.
public synchronized void init(ProcessingEnvironment env) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
@Override
// ์ด๋ ํ ์ด๋
ธํ
์ด์
์ ์ฒ๋ฆฌํ ๊ฑด์ง ์ ํ๋ค.
public Set<String> getSupportedAnnotationTypes() {
return Set.of(Magic.class.getName());
}
@Override
// ์ด๋ค ์์ค์ฝ๋ ๋ฒ์ ์ ์ง์ํ๋์ง ์ ํ๋ค.
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
// ์ด๋
ธํ
์ด์
ํ๋ก์ธ์์ ๋์ ์ฝ๋ ์์ฑ
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
// @Magic์ ๊ฐ์ง๊ณ ์๋ ์์(ํ๋, ๋ฉ์๋, ๋ณ์...)๋ค์ ์ ๋ถ ๊ฐ์ง๊ณ ์จ๋ค.
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Magic.class);
/* ์ด๋
ธํ
์ด์
์ ๋ฐ๋ผ ์ปดํ์ผ ํ์์ ์ถ๊ฐ๋ก ํ ๋์ ์ฝ๋๋ฅผ ์์ฑํ๋ค */
return true; // false๋ฅผ ๋ฐํํ๋ฉด ๋ค์ round๋ฅผ ํ๋ฒ ๋ ์งํํ๋ค.
}
}
์ด๋ ธํ ์ด์ ํ๋ก์ธ์๋ ๋ผ์ด๋(๋จ๊ณ)๋ณ๋ก ๋ฐ๋ณตํด์ ์งํ๋๋ค. process๋ ์๋์ ๊ฐ์ด ๊ตฌํํ ์ ์๋ค. ์ด๋ ๊ฒ ์ป์ ์ด๋ ธํ ์ด์ ์ ์ ๋ณด๋ฅผ element ๊ฐ์ฒด๋ก ๋ฐ์์ ๋ด๊ฐ ์ํ๋ Builder ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์ฝ๋๋ฅผ ์กฐ์ํ ์ ์๋ค.
@Override
// ์ด๋
ธํ
์ด์
ํ๋ก์ธ์๋ round(๋จ๊ณ)๋ฅผ ๋ฐ๋ณตํ๋ฉด์ ๋์ํ๋ค.
// ๊ฐ round๋ง๋ค ํน์ ํ ์ด๋
ธํ
์ด์
์ ์ฒ๋ฆฌํ ํ, ๊ฒฐ๊ณผ๋ฅผ ๋ค์ round๋ก ๋ฐํํ๋ค.
// ์ด๋ ๊ฒ ํฐ๊ทธ๋ฆผ์ผ๋ก Filter-Chaning์ฒ๋ผ ๋์ํ์ง๋ง, ์ค์ ๋์์ ์กฐ๊ธ ๋ ๋ณต์กํ๋ค.
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
// @Magic์ ๊ฐ์ง๊ณ ์๋ ์์(ํ๋, ๋ฉ์๋, ๋ณ์...)๋ค์ ์ ๋ถ ๊ฐ์ง๊ณ ์จ๋ค.
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Magic.class);
for (Element element : elements) {
Name elementName = element.getSimpleName();
if (element.getKind() != ElementKind.INTERFACE) {
// element๊ฐ ์ํ๋ ํ์
์ด ์๋ ๊ฒฝ์ฐ processingEnv๋ฅผ ์ด์ฉํด ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ ์ฝ๋
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"Magic annotation can not be used on " + elementName);
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Processing " + elementName);
}
}
return true // true๋ ๋ชจ๋ ์์
์ด ์๋ฃ๋์๋ค๋ ์๋ฏธ. ์ ๋
ธํ
์ด์
ํ๋ก์ธ์๊ฐ ์ข
๋ฃ๋จ
}
@3 ์ฝ๋ ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(javapoet)
๋ค๋ง ์ด๋ ๊ฒ ์ง์ ๊ตฌํํ๋ฉด ์๋ฐ ์์ค์ฝ๋๋ฅผ ์์ฑํ๊ธฐ๋ ์๋นํ ๋ฒ๊ฑฐ๋ก์ด๋ฐ, ์ด๋ฅผ ์ง๊ด์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ javapoet์ด๋ผ๋ ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋ค. javapoet์ ์์ค์ฝ๋๋ฅผ ์ฝ๊ฒ ์์ฑํ๋ ๋๊ตฌ์ด๋ค.
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
์์ javapoet ์ฝ๋๋ ์๋์ ๊ฐ์ ์๋ฐ์ฝ๋๋ฅผ ์ปดํ์ผ ์์ ์ ์์ฑํด๋ธ๋ค.
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
๋ค๋ง '์์ฑ' ํด๋ด๋ ๊ฒ์ด์ง, ์ด๋ฏธ ์์ฑ๋ ํด๋์ค ์ฝ๋๋ฅผ ๋ง์๋๋ก ๋ณ๊ฒฝํ๋ ๊ฑด lombok์ฒ๋ผ ์ปดํ์ผ๋ฌ๋ฅผ ํดํนํด์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ฐ์๋ ์๋ค.
# ์ ๋ฆฌ
์ ๋ ธํ ์ด์ ํ๋ก์ธ์๋ ์ปดํ์ผ ์์ ์ ์๋ํ๋ ๋น๋ ํด์ด๋ค. ์ฆ ๋ฆฌํ๋ ์ ๊ณผ ๋ค๋ฅด๊ฒ ์ปดํ์ผ ์์ ์ ๋ชจ๋ ์กฐ์์ด ์๋ฃ๋๊ธฐ์, ์ถ๊ฐ์ ์ธ ๋ฐํ์ ๋น์ฉ์ ์๋ค. (๊ทธ๋ฅ ๋์ผํ ๋ฐ์ดํธ์ฝ๋์ด๋ค). ๊ทธ๋์ ๊ธฐ์กด์ ์ฝ๋๋ฅผ ์กฐ์ํ๋๋ผ๋ ๋ฆฌํ๋ ์ ์ ๋นํด ์ํ์ฑ์ด ๋ํ๋ค.
๋ค๋ง ์ ๋ ธํ ์ด์ ํ๋ก์ธ์๋ ์ฝ๋ ์์ฑ์ด ์๋๋ผ ๊ธฐ์กด์ ์ฝ๋๋ฅผ ์์ ํ๊ธฐ๋ ์ด๋ ค์ด๋ฐ, ์ด๋ฅผ ๊ตฌํํ๋ ค๋ฉด lombok์ฒ๋ผ ์ปดํ์ผ๋ฌ ๋ฉ์๋๋ฅผ ํดํนํด์ผํ๋ค๋ ๋จ์ ์ด ์๋ค. ํ์ง๋ง ๋ฏธ๋์ JVM์์๋ ๊ณต์์ ์ธ API๋ก ํ ์ ์๊ฒ ์ธ์ ๊ฐ ๋ฐ๋์ง ์์๊น?
- lombok
- ์ด๋ ธํ ์ด์ ํ๋ก์ธ์ + ์ปดํ์ผ๋ฌ ๋ฉ์๋ ํดํน - ๊ตฌ๊ธ์ AutoService
- ์๋น์ค๋ก๋์ฉ ๋ฆฌ์์ค๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค. java.util.ServiceLoader์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Javapoet ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ์๋ฐ ์ฝ๋๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์ ๋ ธํ ์ด์ ํ๋ก์ธ์๋ฑ์์ ์ฃผ๋ก ์ฌ์ฉ) - @Override
- ์ฅ? ์ด๊ฑด ์ด๋ ธํ ์ด์ ํ๋ก์ธ์๊ฐ ์๋๋ฐ์? ๋ผ๊ณ ํ ์ ์์ง๋ง ์ฌ์ค ์ด๋ฌํ ํ๊ทธ๋ค๋ ์ด๋ ธํ ์ด์ ํ๋ก์ธ์๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํ๋ค. ๊ถ๊ธํ๋ฉด ํด๋น ๋งํฌ์ ๋ค์ด๊ฐ์ ์ฝ์ด๋ณด์
๊ทธ ์ธ์ ์ด๋ ธํ ์ด์ ํ๋ก์ธ์๋ฅผ ์ฌ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค
- Dagger2(์ปดํ์ผ ํ์์ DI ์ ๊ณต, ๊ทผ๋ฐ ๋ณดํต Spring core์ ์๋ IoC, DI, AOP๋ฅผ ์ฌ์ฉ)
- ์๋๋ก์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์๋๋ก์ด๋ OS๋ JVM ๋ฐ์ดํธ์ฝ๋ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก๋ค.)
โ ButterKinfe: @BindView (๋ทฐ ์์ด๋์ ์ ๋ ธํ ์ด์ ๋ถ์ธ ํ๋ ๋ฐ์ธ๋ฉ)
โ DeepLinkDispatch: ํน์ URI ๋งํฌ๋ฅผ Activity๋ก ์ฐ๊ฒฐํ ๋ ์ฌ์ฉ
# ์๊ณ ํ์ จ์ต๋๋ค.
์ด ๊ธ์์๋ ์ ๋ง ๋ง์ ๋ด์ฉ์ ๋ค๋ฃจ์๋ค.
- ๋ฐ์ดํธ์ฝ๋ ์์ฒด๋ฅผ ์กฐ์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ - ASM, Javassist, ByteBuddy(ASM์ผ๋ก ๊ตฌํํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ)
- ๋ฐ์ดํธ์ฝ๋๋ฅผ ๋ฆฌํ๋ ์ ๊ธฐ๋ฒ(Class<T>)๋ฅผ ์ด์ฉํด์ ๋ฐํ์์ ์กฐ์ํ๋ ๋ฐฉ๋ฒ - Java Reflection API
- ์๋ฐ์ ๋ฆฌํ๋ ์
API - ํด๋์ค ์ ๋ณด(๋ฉ์๋, ํ๋, ์ด๋
ธํ
์ด์
, ์์ฑ์..)๋ฅผ ์ฐธ์กฐํ๊ณ ์ฌ์ฉํ๋ ๋ฒ
- ๋ฆฌํ๋ ์ ๊ธฐ๋ฒ์ ํ์ฉํ ๋ค์ด๋๋ฏน ํ๋ก์ ๊ตฌํ - ๋ฐํ์์ ์ผ์ด๋๋ ๋์ ์ธ ์ธ์คํด์ค ๋ฐ์ด๋ฉ
- ๋ค์ด๋๋ฏน ํ๋ก์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Java Proxy, CGlib, ByteBuddy(ํ๋ก์ ์์ฑ์๋ ์ฌ์ฉ๊ฐ๋ฅ)
- ์ปดํ์ผ ์์ ์์์ ๋น๋ ๋๊ตฌ - ์ ๋ ธํ ์ด์ ํ๋ก์ธ์ (AbstractProcessor)
- ์ ๋ ธํ ์ด์ ํ๋ก์ธ์ ๊ตฌํ์ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ - AutoService, Javapoet
์ด์ ์คํ๋ง, ํ์ด๋ฒ๋ค์ดํธ, Junit, Mockito ๊ฐ์ ํ๋ ์์ํฌ๋ค์ด ์ด๋ป๊ฒ ๊ตฌํ๋ฌ๋์ง ๊ฐ์ด ์กํ๋๊ฐ? ์ด๋ฌํ ์ง์๋ค์ด ๋น์ฅ ์๋น์ค๋ฅผ ๋ง๋๋๋ฐ๋ ํ์์์ง๋ง, ๋ ๋์ ์๋ฐ ๊ฐ๋ฐ์๋ก ์ฑ์ฅํ๋๋ฐ ๋์์ ์ค๊ฑฐ๋ผ๊ณ ํ์ ํ๋ค.
'๐๊ธฐ๋ณธ ์ง์ > Java ๊ธฐ๋ณธ์ง์' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
JMX ํด, ์๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ๋ชจ๋ํฐ๋ง ๋๊ตฌ (0) | 2021.09.22 |
---|---|
์๋ฐ์ ์์ฑ์ ๋์๋ฐฉ์ (+Innerํด๋์ค) (0) | 2021.08.22 |
JVM์ ์๋ฆฌ์ ๋ฐ์ดํธ์ฝ๋ (0) | 2021.08.16 |
์์ธ ์ฒ๋ฆฌ์ ๋ค์ํ ๋ฐฉ๋ฒ (0) | 2021.08.16 |
์๋ฐ์ ์ง๋ ฌํ(Serialization)๋? (0) | 2021.08.04 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev