JiwonDev

๋ฐ”์ดํŠธ์ฝ”๋“œ ์กฐ์ž‘(๋ฆฌํ”Œ๋ ‰์…˜, ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ, ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ)

by JiwonDev

์Šคํ”„๋ง์—์„œ ๋‹ค์–‘ํ•œ ์„ค์ •๊ธฐ๋Šฅ๋“ค (@Autowried ๋“ฑ)์€ ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ์—†์ด ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๊ณ , ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ผ๊นŒ? ๋˜ Lombok๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ? ๊ทธ ๋น„๋ฐ€์€ ๋ชจ๋‘ ์ž๋ฐ”์˜ ๋ฐ”์ดํŠธ์ฝ”๋“œ์— ์žˆ๋‹ค.

2021.08.16 - [๊ธฐ๋ณธ ์ง€์‹/Java ๊ธฐ๋ณธ์ง€์‹] - JVM์˜ ์›๋ฆฌ์™€ ๋ฐ”์ดํŠธ์ฝ”๋“œ

 

JVM์˜ ์›๋ฆฌ์™€ ๋ฐ”์ดํŠธ์ฝ”๋“œ

ํ•ด๋‹น ๊ธ€์€ https://www.inflearn.com/course/the-java-code-manipulation/ ๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. #1 JVM ์šฉ์–ด ์ •๋ฆฌ 2021.06.30 - [๊ธฐ๋ณธ ์ง€์‹/Java ๊ธฐ๋ณธ์ง€์‹] - JVM(์ž๋ฐ”๊ฐ€์ƒ๋จธ์‹ )๊ณผ JRE, JDK ์ž๋ฐ” ๋ฐ”์ด..

jiwondev.tistory.com

Java Reflection - Dynamic Proxies (jenkov.com)

 

Java Reflection - Dynamic Proxies

This text explains how to create dynamic interface implementations at runtime using Java Reflections Dynamic Proxy capabilities

tutorials.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>๋Š” ํด๋ž˜์Šค์— ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 


@ ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ ์–ด๋…ธํ…Œ์ด์…˜

๋ˆˆ์ฐ๋ฏธ๊ฐ€ ์ข‹๋‹ค๋ฉด ์œ„์—์„œ ๋ดค๊ฒ ์ง€๋งŒ, Class<T>์—๋Š” getAnnotation()์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ํด๋ž˜์Šค์— ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ฌ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๋ฉด ์–ด๋…ธํ…Œ์ด์…˜๋“ค์ด ๋‚˜์™€์•ผํ•  ๊ฒƒ ๊ฐ™์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์•„๋ฌด๋Ÿฐ ๊ฐ’๋„ ์ฐํžˆ์ง€ ์•Š๋Š”๋‹ค.

 // ๋“ฑ๋ก๋œ ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์Œ. ์ฝ˜์†”์ฐฝ์— ์ฐ์–ด๋„ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ๋‚˜์˜จ๋‹ค.
 Arrays.stream(User.class.getAnnotations()).forEach(System.out::println);

๊ทธ ์ด์œ ๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ์ž์ฒด๊ฐ€ ์ฃผ์„ (//)๊ณผ ๋™์ผํ•œ ์ทจ๊ธ‰์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์œ„ํ•œ ์ฃผ์„.

 

์ž๋ฐ” ์–ด๋…ธํ…Œ์ด์…˜(@Annotation)

์ž๋ฐ”์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ (@Annotation )์€ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์œ„ํ•œ ์ผ์ข…์˜ ์ฃผ์„์œผ๋กœ, [๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์œผ๋ฉฐ] ์ปดํŒŒ์ผ ๊ณผ์ •๊ณผ ์‹คํ–‰๊ณผ์ •์—์„œ ์–ด๋–ป๊ฒŒ ์ถ”๊ฐ€์ ์ธ ์ฒ˜๋ฆฌ & ์ปดํŒŒ

jiwondev.tistory.com

@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() ๋ฉ”์„œ๋“œ๋งŒ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์—ฌ ์กฐ์ž‘ํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๋ฉด, ํŠน์ • ๋ฉ”์„œ๋“œ๋‚˜ @์–ด๋…ธํ…Œ์ด์…˜ ๊ฐ’์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง€๋Š” ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. => ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ

Proxy ๊ฐ์ฒด์˜ IH(Invoke Handler)๋งŒ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ตฌํ˜„ํ•˜์—ฌ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” API๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.

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์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด๋ผ์„œ ์„ฑ๋Šฅ์ด ์กฐ๊ธˆ ๋–จ์–ด์ง„๋‹ค.

https://www.baeldung.com/spring-aop-vs-aspectj


@ ์Šคํ”„๋ง 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๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ๋ฌธ๋ฒ• ์—๋Ÿฌ๋ฅผ ํ™•์‹คํ•˜๊ฒŒ ์ฒดํฌํ•˜๋„๋ก ์ •๋ณด ์ œ๊ณต (@Override๋“ฑ)
  2. ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ž๋™์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ƒ์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด ์ œ๊ณต (์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ)
  3. ๋Ÿฐํƒ€์ž„์— ๋™์ ์œผ๋กœ ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ •๋ณด๋ฅผ ์ œ๊ณต (๋ฆฌํ”Œ๋ ‰์…˜)

 


@ 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)๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ๋Š” Javac(์ž๋ฐ” ์ปดํŒŒ์ผ๋Ÿฌ)์— ํฌํ•จ๋˜์–ด์žˆ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด๋‹ค.

 


@ Lombok์˜ ๋™์ž‘์›๋ฆฌ

  • ์šฐ์„  lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ์ „์šฉ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • [์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ]๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๋‚ด๋ถ€์—์„œ AbstractProcessor๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์• ๋…ธํ…Œ์ด์…˜๋“ค๋กœ ์ ์€ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ปดํŒŒ์ผ ์‹œ์ ์— [์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ]๋ฅผ ์ด์šฉํ•˜์—ฌ ์†Œ์Šค์ฝ”๋“œ์˜ AST(abstract syntax tree)๋ฅผ ์กฐ์ž‘ํ•œ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด์„œ ๋งˆ์น˜ ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋€ ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.
    AST๊ฐ์ฒด. javac(์ปดํŒŒ์ผ๋Ÿฌ)์—์„œ๋Š” ๋‚ด๋ถ€ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ AST๋ฅผ ๋งŒ๋“ ๋‹ค.
  • ๋‹ค๋งŒ ์ด๋Š” ์ž๋ฐ”์—์„œ ๊ณต์‹์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์ฐธ์กฐ๋งŒ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด(TypeElement ์™€ RoundEnvironment)๋ฅผ ํ•˜์œ„ํƒ€์ž…์œผ๋กœ ๊ฐ•์ œ ์บ์ŠคํŒ…ํ•˜์—ฌ javac๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ AST๋ฅผ ์กฐ์ž‘ํ•œ๋‹ค. ์ผ์ข…์˜ ์ปดํŒŒ์ผ๋Ÿฌ ํ•ดํ‚น์ด๋ผ๊ณ  ๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค.
  • ๊ทธ๋ž˜์„œ ์ดํด๋ฆฝ์Šค๋Š” agent๋กœ, VsCode์™€ IntelliJ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ lombok์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณตํ•ด์ค€๋‹ค.
    (IntelliJ์—์„œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๊ณ , Lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ importํ•œ ํ›„ ์–ด๋…ธํ…Œ์ด์…˜ํ”„๋กœ์„ธ์Šค๋ฅผ ํ™œ์„ฑํ™” ์‹œํ‚ค๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.)

์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์Šค๋Š” javax.annotation.processing.AbstractProcessor ๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  Lombok์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ , ๋…ผ๋ž€๊ฑฐ๋ฆฌ

๋”๋ณด๊ธฐ

Lombok์€ ๊ณต๊ฐœ๋œ API๊ฐ€ ์•„๋‹Œ ์ปดํŒŒ์ผ๋Ÿฌ ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์กด ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•œ๋‹ค.

 

์‹ฌ์ง€์–ด ์ดํด๋ฆฝ์Šค์˜ Lombok์€ java agent๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํŒŒ์ผ๋Ÿฌ ๋‚ด๋ถ€ ํด๋ž˜์Šค ์ž์ฒด๋„ ์กฐ์ž‘ํ•ด๋ฒ„๋ฆฐ๋‹ค. ํ•ด๋‹น ํด๋ž˜์Šค๋“ค ์—ญ์‹œ ๊ณต์‹์ ์œผ๋กœ ๊ณต๊ฐœ๋œ API๊ฐ€ ์•„๋‹ˆ๋‹ค๋ณด๋‹ˆ, JVM์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด์„œ ๋ฒ„์ „ ํ˜ธํ™˜์„ฑ๋“ฑ ์ •๋ง ๋‹ค์–‘ํ•œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š” ์œ„ํ—˜์„ฑ์„ ๊ฐ€์ง„๋‹ค.

 

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  lombok์ด ์ œ๊ณตํ•˜๋Š” ์—„์ฒญ๋‚œ ํŽธ์˜๊ธฐ๋Šฅ๋“ค ๋•๋ถ„์— ์••๋„์ ์ธ ์‚ฌ์šฉ๋ฅ ์„ ๋ณด์ด๊ณ  ์žˆ๋‹ค. ๋ฌผ๋ก  ์ž๋ฐ” ๊ณต์‹API๋งŒ์„ ์‚ฌ์šฉํ•œ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ์žˆ์ง€๋งŒ, lombok๋งŒํผ ํŽธํ•˜์ง€๋Š” ์•Š์•„์„œ ๋Œ€์ฒดํ•˜์ง€ ๋ชปํ•˜๋Š” ํ˜„์‹ค์ด๋‹ค.

 

์‚ฌ์‹ค ์ด๋Ÿฌํ•œ ๋…ผ๋ž€์€ ๋กฌ๋ณต ๊ณต์‹ํ™ˆํŽ˜์ด์ง€์™€ Lombok์„ ๋งŒ๋“  ๊ฐœ๋ฐœ์ž์˜ ๋ธ”๋กœ๊ทธ์—๋„ ์ •์ƒ์ ์ธ ์ž๋ฐ”API ์‚ฌ์šฉ์ด ์•„๋‹Œ ํ•ดํ‚น์ž„์„ ์ธ์ •ํ•˜๊ณ  ์žˆ๊ณ , ๊ฐœ๋ฐœ์ž์˜ ์ž…์žฅ์ด ์ž์„ธํ•˜๊ฒŒ ์ ํ˜€์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ์˜ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋ ค๊ณ  ํ•˜๋Š”๊ฑฐ์ง€ Lombok์— ๋Œ€ํ•ด ์•Œ๊ณ ์žํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๊ธฐ์—, ์ด์ •๋„๋งŒ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€๋„๋ก ํ•˜์ž.

์ž๋ฐ”API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ Javac ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ•์ œ๋กœ ์บ์ŠคํŒ…ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ์— ์ปดํŒŒ์ผ๋Ÿฌ ํ•ดํ‚น์ด ๋งž๋‹ค.

 


 

# ์• ๋…ธํ…Œ์ด์…˜ ํ”„๋กœ์„ธ์„œ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

@ 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 ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์ด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„๋ฌ๋Š”์ง€ ๊ฐ์ด ์žกํžˆ๋Š”๊ฐ€? ์ด๋Ÿฌํ•œ ์ง€์‹๋“ค์ด ๋‹น์žฅ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ๋Š” ํ•„์š”์—†์ง€๋งŒ, ๋” ๋‚˜์€ ์ž๋ฐ” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๋Š”๋ฐ ๋„์›€์„ ์ค„๊ฑฐ๋ผ๊ณ  ํ™•์‹ ํ•œ๋‹ค.

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ