🍡 java

JAVA | λ¦¬ν”Œλ ‰μ…˜(Reflection) μ•ŒκΈ°

c0zi 2023. 7. 5. 21:48

λ¦¬ν”Œλ ‰μ…˜μ΄λž€ ?


1. λ¦¬ν”Œλ ‰μ…˜(Reflection)

: μ‹€ν–‰ 쀑인 ν”„λ‘œκ·Έλž¨μ˜ 클래슀, λ©”μ„œλ“œ, ν•„λ“œ 등에 λŒ€ν•œ 정보λ₯Ό μ–»κ±°λ‚˜ μ‘°μž‘ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” API

  • 클래슀의 정보 뢄석, μΈμŠ€ν„΄μŠ€ 생성, λ©”μ„œλ“œ 호좜, ν•„λ“œμ˜ 값을 κ°€μ Έμ˜€κ±°λ‚˜ μ„€μ • λ“±μ˜ μž‘μ—… μˆ˜ν–‰

 

2. λ¦¬ν”Œλ ‰μ…˜μ΄ μ΄μš©λ˜λŠ” 상황

1. λŸ°νƒ€μž„ μ‹œμ— λ™μ μœΌλ‘œ 클래슀λ₯Ό λ‘œλ“œν•΄μ•Ό ν•˜λŠ” 경우

2. 클래슀의 ꡬ쑰, λ©”μ„œλ“œ, ν•„λ“œ 등에 λŒ€ν•œ 정보λ₯Ό κ°€μ Έμ™€μ„œ 뢄석해야 ν•˜λŠ” 경우

3. 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ±°λ‚˜ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜κ±°λ‚˜ ν•„λ“œμ˜ 값을 λ³€κ²½ν•΄μ•Ό ν•˜λŠ” 경우

 

μœ„μ˜ μ„€λͺ…μœΌλ‘œλŠ” 이해가 μ–΄λ €μš°λ‹ˆ 쑰금 더 μ•Œμ•„λ³΄λ„λ‘ ν•˜μž.

 


# CASE 1


첫번째 κ²½μš°μ—μ„œ, λ‹€μŒκ³Ό 같이 1μ°¨ κ°œλ°œμžλŠ” "/login"을 μž…λ ₯ λ°›μ•˜μ„ λ•Œ uc.login() λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜λ„λ‘,

"/join"을 μž…λ ₯ λ°›μ•˜μ„ λ•Œ uc.join() λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜λ„λ‘ ν•˜λŠ” application을 λ§Œλ“€μ—ˆλ‹€.

 

import java.util.Scanner;

// 1차 개발자
public class App {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String path = sc.nextLine();

        // path = "/login" -> uc.login()
        // path = "/join" -> uc.join();
        UserController uc = new UserController();

        if(path.equals("/login")){
            uc.login();
        }else if(path.equals("/join")){
            uc.join();
        }
    }
}

 

이 ν”„λ‘œκ·Έλž¨μ€ μ‹€ν–‰ μ‹œμΌ°μ„ λ•Œ 잘 λŒμ•„κ°€κΈ° λ•Œλ¬Έμ— 잘 λ§Œλ“€μ–΄μ§„ ν”„λ‘œκ·Έλž¨μ΄λΌκ³  λ³Ό 수 μžˆλ‹€.

 

κ·Έλ ‡λ‹€λ©΄, 이와 같은 ν”„λ‘œκ·Έλž¨μ„ 2μ°¨ κ°œλ°œμžκ°€ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 무슨 정보가 ν•„μš”ν• κΉŒ ?

 

λ‹€μŒκ³Ό 같이 1. λ©”μ„œλ“œ λͺ…κ³Ό 2. 클래슀 λͺ…이 ν•„μš”ν•  것이닀.

 

// 2차 개발자
public class UserController {
    public void login(){
        System.out.println("login() 호좜됨");
    }

    public void join(){
        System.out.println("join() 호좜됨");
    }
}

 

ν•˜μ§€λ§Œ 1μ°¨ κ°œλ°œμžλŠ” 일일이 λ©”μ„œλ“œ λͺ…κ³Ό 클래슀 λͺ…을 μ „λ‹¬ν•˜λŠ” 것보닀 interfaceλ₯Ό 톡해 λ©”μ„œλ“œλ₯Ό μž‘μ„±ν•˜κ³ , overrideλ₯Ό 톡해 κ΅¬ν˜„ν•˜λ„λ‘ ν•˜λŠ” 것이 더 νŽΈλ¦¬ν•  것이닀.

 

/*
	μž‘μ„±μž : 홍길동
    μ£Όμ˜μ‚¬ν•­ : κ΅¬ν˜„μ²΄λŠ” UserController둜 ν•΄μ£Όμ„Έμš” 
*/

public interface UserCon {
	void login();
	void join();
}

 

그런데 μ—¬κΈ°μ„œ 2μ°¨ κ°œλ°œμžκ°€ μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•˜κΈ° μœ„ν•΄ λ©”μ„œλ“œλ₯Ό μƒˆλ‘œ μƒμ„±ν•œλ‹€λ©΄ μ–΄λ–»κ²Œ 될까 ?

 

2μ°¨ κ°œλ°œμžκ°€ μƒμ„±ν•œ λ©”μ„œλ“œλŠ” λ‹Ήμ—°νžˆ μž‘λ™λ˜μ§€ μ•Šμ„ κ±°κ³  이와 같은 상황을 ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œλŠ” 1μ°¨ κ°œλ°œμžκ°€ μž‘μ„±ν•œ interfaceλ₯Ό 같이 μˆ˜μ •ν•΄μ€˜μ•Ό ν•œλ‹€. (λΆˆν•„μš”ν•œ μœ μ§€λ³΄μˆ˜)

 

κ²°κ΅­ ν˜‘μ—…μ΄ μ•„λ‹Œ, 판맀λ₯Ό μœ„ν•œ ν”„λ‘œκ·Έλž¨μ΄λΌλ©΄ 이 ν”„λ‘œκ·Έλž¨μ€ νŒλ§€κ°€ λ˜μ§€ μ•Šμ„ 것이닀. (OCP μœ„λ°° ?)

 


# CASE 2


λ‘λ²ˆμ§Έ κ²½μš°μ΄λ‹€. μ΄λ²ˆμ—λŠ” μ—λ„ˆν…Œμ΄μ…˜μ„ ν™œμš©ν•΄λ³΄λ €κ³  ν•œλ‹€.

 

μ•„λž˜μ˜ μ—λ„ˆν…Œμ΄μ…˜μ€ λ©”μ„œλ“œκ°€ 싀행될 λ•Œ uri의 μ£Όμ†Œλ₯Ό λΆ„μ„ν•˜λŠ” μ—λ„ˆν…Œμ΄μ…˜μ΄λ‹€.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) // μœ„μΉ˜ = λ©”μ„œλ“œ
@Retention(RetentionPolicy.RUNTIME) // 타이밍 = μ‹€ν–‰μ‹œ
public @interface RequestMapping {
    String uri();
}

 

κ·Έ 결과둜 @RequestMapping에 uriκ°€ μ–΄λ–»κ²Œ λ“€μ–΄μ˜€λŠ”μ§€μ— 따라 λ‹€λ₯Έ λ©”μ„œλ“œλ₯Ό μ‹€ν–‰ν•˜κ²Œ ν•œλ‹€.

 

public class UserController {
    @RequestMapping(uri = "/login")
    public void login(){
        System.out.println("login() 호좜됨");
    }

    @RequestMapping(uri = "/join")
    public void join(){
        System.out.println("join() 호좜됨");
    }
}

 

λ‹€μŒ App ν΄λž˜μŠ€μ—μ„œ scannerλ₯Ό 톡해 uri값을 μž…λ ₯ λ°›κ³  findUri둜 UserController() 객체와 uriκ°€ λ§€κ°œλ³€μˆ˜λ‘œ λ“€μ–΄κ°„λ‹€.

findUriλŠ” uriλ₯Ό 뢄석해 컨트둀러의 λ©”μ„œλ“œλ₯Ό μ‹€ν–‰ν•œλ‹€.

 

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class App {

    // λ‚΄μš© 뢄석해보기 혼자 !!!!!!!!
    // uri 뢄석해 컨트둀러의 λ©”μ„œλ“œ μ‹€ν–‰ν•΄
    public static void findUri(UserController uc, String uri) throws Exception {
        boolean isFind = false;

        Method[] methods = uc.getClass().getDeclaredMethods(); // 2개

        for (Method mt : methods) {
            Annotation anno = mt.getDeclaredAnnotation(RequestMapping.class);
            RequestMapping rm = (RequestMapping) anno;
            if (rm.uri().equals(uri)) {
                isFind = true;
                mt.invoke(uc); // ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ‹€ν–‰ μ‹œμΌœλΌ
            }
        }

        if(isFind == false){
            System.out.println("404 Not Found");
        }
    }

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        String uri = sc.nextLine();

        findUri(new UserController(), uri);

    }

}

 

μ΄μ œλŠ” UserController μ•ˆμ—μ„œ μƒˆλ‘œμš΄ λ©”μ„œλ“œλ₯Ό 생성해도 @RequestMapping을 뢙이면 싀행이 되기 λ•Œλ¬Έμ— νŒλ§€ν•  수 μžˆλŠ” ν”„λ‘œκ·Έλž¨μ΄ 된 것 같기도 ν•˜λ‹€.

 

 

ν•˜μ§€λ§Œ, μƒˆλ‘œμš΄ 컨트둀러λ₯Ό λ§Œλ“€κ³  μ‹Άλ‹€λ©΄?

 

public class BoardController {

	@RequestMapping(uri = "/save")
    public void save() { System.out.println("save() 호좜됨"); }
    
}

 

App ν΄λž˜μŠ€μ—μ„œ UserController λ©”μ„œλ“œλ§Œ findUri둜 μ²˜λ¦¬ν•˜κ³  있기 λ•Œλ¬Έμ—, 이 BoardControllerλ₯Ό ν˜ΈμΆœν•  수 μžˆλŠ” 방법은 μ—†λ‹€. 


# CASE 3


import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class App {

    public static Set<Class> componentScan(String pkg) throws Exception {

        // ν˜„μž¬ App 파일의 경둜
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set<Class> classes = new HashSet<>();

        URL packageUrl = classLoader.getResource(pkg);
        File packageDirectory = new File(packageUrl.toURI());
        for (File file : packageDirectory.listFiles()) {
            if (file.getName().endsWith(".class")) {
                String className = pkg + "." + file.getName().replace(".class", "");
                //System.out.println(className);
                Class cls = Class.forName(className);
                classes.add(cls);
            }
        }
        return classes;
    }

    public static void findUri(Set<Class> classes, String uri) throws Exception {
        boolean isFind = false;
        for (Class cls : classes) {
            if (cls.isAnnotationPresent(Controller.class)) {
                Object instance = cls.newInstance();
                Method[] methods = cls.getDeclaredMethods();

                for (Method mt : methods) {
                    Annotation anno = mt.getDeclaredAnnotation(RequestMapping.class);
                    RequestMapping rm = (RequestMapping) anno;
                    if (rm.uri().equals(uri)) {
                        isFind = true;
                        mt.invoke(instance);
                    }
                }
            }
        }
        if(isFind == false){
            System.out.println("404 Not Found");
        }
    }

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        String uri = sc.nextLine();

        Set<Class> classes = componentScan("ex03");
        findUri(classes, uri);

    }

}

 

이제 변경사항이 λˆˆμ— λŒν…λ°, componentScan λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄ package 내에 μžˆλŠ” λͺ¨λ“  클래슀λ₯Ό classes에 λ„£λŠ”λ‹€.

 

μ΄λ•Œμ˜ componentScan λ©”μ„œλ“œμ—μ„œλŠ” 

 

1, Thread.currentThread().getContext.ClassLoader()λ₯Ό μ΄μš©ν•˜μ—¬ ν˜„μž¬ μ‹€ν–‰ 쀑인 μ“°λ ˆλ“œμ˜ 클래슀 λ‘œλ”λ₯Ό μ–»μ–΄μ™€μ„œ ν•΄λ‹Ή 클래슀 λ‘œλ”μ—κ²Œ resource μ°ΎλŠ” 역할을 λΆ€μ—¬ν•˜κ³ ,

2. classesλΌλŠ” hashset을 μƒμ„±ν•œλ‹€.

3. λ¦¬μ†ŒμŠ€μ—μ„œ ν•΄λ‹Ή νŒ¨ν‚€μ§€μ˜ Url을 읽어 packageDirectory에 μ €μž₯ν•œλ‹€.

4. 이 λ””λ ‰ν† λ¦¬μ˜ νŒŒμΌλ“€μ„ for문을 톡해 .class둜 λλ‚˜λŠ” νŒŒμΌλ“€λ§Œ μ°ΎλŠ”λ‹€.

5. νŒŒμΌλ“€μ„ κ·Έλ ‡κ²Œ 찾은 class듀을 classes에 μ €μž₯ν•œλ‹€. 

 

그리고 findUriλŠ” ν΄λž˜μŠ€λ“€μ˜ 갯수만큼 for문을 톡해 λ°˜λ³΅ν•˜κ³  ν•΄λ‹Ή 클래슀의 μ–΄λ…Έν…Œμ΄μ…˜μ΄ controller이면 clc.newInstance()λ₯Ό 톡해 λ©”λͺ¨λ¦¬λ₯Ό λ„μš΄λ‹€. (동적 뢄석)

κ·Έ λ’€λŠ” case2의 κ²½μš°μ™€ 같이 λ©”μ„œλ“œλ₯Ό λ°˜λ³΅ν•˜λ©° uriλ₯Ό ν™•μΈν•˜κ³  λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.

 

이와 같이 λ³€κ²½ν•˜κ²Œ 되면, λ‹€λ₯Έ κ°œλ°œμžλ“€μ΄ μƒˆλ‘œμš΄ controllerλ₯Ό λ§Œλ“€λ“  λ©”μ„œλ“œλ₯Ό λ§Œλ“€λ“  λͺ¨λ‘ μ μ ˆν•˜κ²Œ μ‚¬μš©λ  수 μžˆλ‹€.

 


κ²°λ‘ 


μžλ°”μ—μ„œμ˜ λ¦¬ν”Œλ ‰μ…˜

ꡬ체적인 ν΄λž˜μŠ€ νƒ€μž…μ„ μ•Œμ§€ λͺ»ν•΄λ„ κ·Έ 클래슀의 λ©”μ†Œλ“œμ™€ νƒ€μž… 그리고 λ³€μˆ˜λ“€μ„ μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•΄μ£ΌλŠ” μžλ°” API 이닀.

> μœ„μ˜ μ˜ˆμ‹œμ—μ„œ CASE 3의 componentScan λ©”μ„œλ“œκ°€ ν•˜λŠ” 일을 λ¦¬ν”Œλ ‰μ…˜μ΄λΌκ³  ν•  수 μžˆλ‹€.