리플렉션 예제

Jan 30, 2024
리플렉션 예제

예제 링크 및 초반 세팅

[ 예제 ] https://github.com/codingspecialist/java-reflection/tree/main/src
notion image
+) setting에 들어가서 gradle에 한글 패치 하자
일단, 내가 디스패처를 만들어주는 사람이라고 생각하자. 그걸 A 회사에 팔았는데 새로운 함수를 만들때마다 내가 if해서 만들어주고, if해서 만들어주고… 귀찮겠지? 리플렉션을 달아서 주는 것임!
 

예제 1 - 리플렉션 X

notion image
App과 UserController 클래스 생성
notion image
/login이 들어오면 호출 시킬 login()과 /join이 들어오면 호출 시킬 join() 메소드 생성
notion image
notion image
라우터에서 /login을 치니까 login()이 정상 작동되는 것 확인! 그러나...
notion image
이렇게 새로운 메소드 userinfo()를 만들면 죽어도 실행 안된다. 라우터에 path.equals("/userinfo")) 가 없기 때문... 울면서 유지보수 부탁한다고 계속 전화해야한다. 이걸 리플렉션을 써서 해결해보자
 

예제 2 - 리플렉션 O (+@RequestMapping 어노테이션) ✓

[ @RequestMapping 어노테이션 ]

notion image
자바 클래스 생성을 하는데... 선택을 'Annotation' 으로 한다!!! Annotation을 활용하여 리플렉션 해야한다!!!
💡
@RequestMapping (어떤 요청이 들어오는걸 매핑에서 팍 해주는 어노테이션) 스프링 프레임워크에서 사용되는 어노테이션 중 하나로, 웹 애플리케이션에서 어떤 URL 경로에 대한 요청을 처리할 클래스/메서드 등을 지정하는 역할. 이렇게 지정된 메서드는 사용자가 해당 URL 경로로 요청을 보내면 실행되어 그에 맞는 작업을 처리하고 결과를 반환해준다.
notion image
어노테이션을 생성하면 이렇게 나온다. 우리가 어노테이션을 만들어볼 것이다! (근데... 스프링가면 팍 하면 파바박 나온다. 이후엔 할 필요 X)
 

[ Annotation 생성 ]

notion image
먼저 어노테이션의 타겟을 지정해주자. ElementType.FIELD = 필드 위에 어노테이션을 붙일 수 있고 ElementType.CONSTRUCTOR = 생성자 위에 붙일 수 있고 ElementType.METHOD = 메소드 위에 붙일 수 있고 원하는걸 선택할 수 있다. 우린 메서드 위에 붙일 것이니 [ ElementType.METHOD ] 선택
notion image
이젠 발동 시점을 선택해줄 차례 보통 @Override는 컴파일 시점에 발동하잖아. 이 컴파일 시점에 작동하는건 툴이 보잖아? 그래서 부모랑 자식의 메소드가 다르면 오류가 나버린다. 그러니 우리는 RetentionPolicy에 있는 'RUNTIME' 시 발동하게 만들 것이다!
💡
RUNTIME 어노테이션은 컴파일된 클래스 파일에 포함되고, 런타임 시에도 사용 가능
💡
이렇게 우리는 어노테이션을 [ 메서드 위, 런타임 시 실행 ] 되도록 만들었다.
 

[ RequestMapping 완성 ]

notion image
String uri();는 RequestMapping 어노테이션의 멤버 메소드 이 메소드는 사용자가 요청을 보낼 URL 경로를 지정하기 위해 사용한다. @RequestMapping(uri = "/users")와 같이 사용하면, "/users"라는 URL 경로로 들어오는 요청을 해당 메소드가 처리한다는 말.
 

[ Annotation 사용 ]

App 만든 사람은 a개발자, userController를 만든 사람은 b개발자라고 가정한다

1. 먼저 리플렉션을 사용해보자

notion image
Method[] methods = con.getClass().getDeclaredMethods(); UserController객체의 클래스를 들고 와서 거기 안에 선언된 메서드들을 다 들고 오는 코드! (해당 클래스에 선언된 모든 메소드들을 배열 형태로 반환한다)
notion image
notion image
현재 메소드가 3개 있으니 결과로 3이 뜬다. 이제 리플렉션을 해보자
notion image
for-each 반복문을 사용하여 methods 배열의 각 메소드에 접근한다. method.getName()을 호출하여 각 메소드의 이름을 가져온 후, System.out.println()을 사용하여 이름을 출력함
💡
for문 돌면서 한 개씩 분석할 거라 Method임
notion image
UserController클래스에 선언된 모든 메소드들의 이름을 출력하는 리플렉션 코드를 봤다 이제 아까 만들었던 어노테이션을 걸어보자
 

2. 이제 Annotation 사용해보자

notion image
uri 쓰라고 뜨죠? 써줍니다.
notion image
이렇게! 우리가 만든 어노테이션을 넣어줬다 이 어노테이션이 붙어있으면 호출해!! 하는 것 그러니 userinfo는 혼자 분석되지 않겠지...
 

 
notion image
어노테이션은 @GETTER 처럼 메소드에 여러 개를 붙일 수 있다. 지금은 한 개만 붙일거라 끝에 's'가 붙지 않은걸 선택
notion image
💡
이 코드 치는게… 라우터죠?
methods 배열에 저장된 메서드들을 순회하면서, 각 메서드에 RequestMapping 어노테이션이 있는지 확인하고, RequestMapping 어노테이션이 있는 메서드의 이름을 출력하는 코드! (* 만약 존재하지 않는다면 anno에 null이 할당) 첫 번째 메서드에서 RequestMapping 어노테이션이 존재하고, 해당 어노테이션의 URL인 "login"이 anno 변수에 담겼을 것이다.

[ Annotation anno 를 일단 이렇게 바꾸자 ]

RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); 이거는 다른 어노테이션이 잇으면 받다가 터질 수 있음 (지금은 ㄱㅊ)

[ RequestMapping rm ] - 바로 사용 가능

rm 변수를 통해 RequestMapping 어노테이션에 정의된 메서드와 속성에 바로 접근 가능 (즉, rm 변수를 사용하여 RequestMapping 어노테이션의 정보를 바로 활용 가능)

[ Annotation anno ] - 형변환 필요

Annotation은 모든 어노테이션의 상위 타입이기 때문에, anno 변수를 통해 RequestMapping 어노테이션의 정보에 접근하려면 추가적인 형변환을 해야한다. 즉, 형변환을 통해 RequestMapping 어노테이션의 메서드와 속성에 접근해야함!!
 

 
💡
마찬가지로 for문 돌면서 한 개씩 분석할 거라 Method임
💡
절대 못 외우니 코드 보고해라
 

2-1. invoke()

notion image
invoke()는 리플렉션을 사용하여 메서드를 호출하는 메서드 일반적으로 메서드를 호출할 때 메서드의 이름을 사용하여 호출하지만, invoke()를 사용하면 프로그램 실행 중에 메서드를 동적으로 호출할 수 있다 (=리플렉션 가능!) * 하나하나 돌면서 path랑 uri가 동일하면 invoke가 때리겠단 말
invoke() 메서드를 사용할 때는 호출하려는 메서드를 가지고 있는 객체... 즉, Controller를 invoke() 메서드의 첫 번째 인자로 전달! +) new가 100개면, 그 100개의 heap을 구분하기 위해서 어떤 heap의 메소드를 때려야하는지 알려주기 위해서 method.invoke에 con을 넣음...
💡
method.invoke(con); = con.login()
notion image
try-catch 해준다
 

 
notion image
이거 왜 NullException이!? 3번째 메소드인 userinfo()에 어노테이션이 없어서! 3번째에 터져서 저런 null 오류가 생김! 작동이 어떻게 되냐면, 2번째 메소드는 rm == null 아니죠? 근데 path값이 같지 않기 때문에 실행은 안됨 3번째 메소드는 rm == null 이죠? 그래서 null이 터졌어요
💡
리플렉션은 순서가 랜덤이라 뭐부터 실행될 지는 모름 UserController 메소드를 분석하는데, 메소드 3개 중 뭘 먼저 찾을지 모른다. 랜덤~ 랜덤~
notion image
그래서 이렇게 해줬다. userinfo를 찾으러 갔는데 어노테이션이 없어서 터짐 null 그러니 continue를 하죠? (* 이후에 적힌 코드들을 무시하고 다시 처음으로 돌아감) 이번엔 join을 찾았는데… path가 다르죠? invoke아니죠? 그래서 login을 찾았는데.. invoke죠? break !
 

2-2. userinfo도 어노테이션을…

notion image
userinfo를 돌리면 아무 값도 안 나오니까 추가해주자!
notion image
똑같이 어노테이션 해주면?
notion image
짠! 어노테이션을 붙이면 라이브러리에 없는 메소드가 백개, 천개여도 실행이 된단 것!
💡
path는 이후 클라이언트가 보내는 것
 

전체 코드

[ App ]

package ex02; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class App { public static void main(String[] args) { String path = "/userinfo"; UserController con = new UserController(); //UserController의 클래스를 들고와서 거기 안에 있는 메서드 다 들고와 Method[] methods = con.getClass().getDeclaredMethods(); // System.out.println(methods.length); // for (Method method : methods) { RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); if (rm == null) continue; if (rm.uri().equals(path)) { try { method.invoke(con); // =con.login()이랑 같은 코드임 break; } catch (Exception e) { e.printStackTrace(); } } } } }

[ UserController ]

package ex02; public class UserController { @RequestMapping(uri="/login") public void login() { System.out.println("로그인 호출됨"); } @RequestMapping(uri="/join") public void join() { System.out.println("회원가입 호출됨"); } @RequestMapping(uri="/userinfo") public void userinfo() { System.out.println("유저 정보 보기"); } }

[ RequestMapping ]

package ex02; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequestMapping { String uri(); //i=아이덴티파이 끝에 붙어있는 /슬래시 뭐 }
 
💡
이제 각 테이블에 대한 관리를 담당하는 컨트롤러를 만들어보자. 모듈화와 유지ㆍ보수 측면에서 유리할 것, 그러나 컨트롤러의 개수가 늘어나면 관리가 어렵다 ㅠㅠ
 

예제 3 - 컨트롤러 어노테이션 생성 (ㅠㅠ…)

이제... 파일이 아니라 패키지를 리플렉션으로 분석하고, 해당 패키지 내에 있는 모든 컨트롤러 클래스를 찾아서 실행할 것이다!! 패키지를 리플렉션으로 분석하고 해당 패키지 내의 컨트롤러 클래스를 찾은 후에는, 원하는 컨트롤러를 선택하여 인스턴스화하고 실행할 수 있다. 이를 통해 경로만 변경해도 다양한 컨트롤러를 실행할 수 있다.

리팩토링 코드

package ex03; import java.lang.reflect.Method; public class App { public static void findUri(UserController con, String path) { //UserController의 클래스를 들고와서 거기 안에 있는 메서드 다 들고와 Method[] methods = con.getClass().getDeclaredMethods(); for (Method method : methods) { RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); if (rm == null) continue; if (rm.uri().equals(path)) { try { method.invoke(con); // =con.login()이랑 같은 코드임 break; } catch (Exception e) { e.printStackTrace(); } } } } public static void main(String[] args) { findUri(new UserController(), "/login"); } }
메서드를 메인에서 뺐습니다. 클래스로 올렸네요
notion image
path만 바꾸면 다 실행될 것이다. 그런데… UserController() 만 리플렉션 되면 안되겠지? 패키지에서 리플렉션 받아올 것!
 

[ Controller 어노테이션 생성 ]

notion image
notion image
이번엔 클래스 위에 어노테이션을 붙이도록 만들어준다.
패키지를 분석해서 @Controller가 붙어있는 애들을 전부 new 한 후, set자료형에 담을 것 * Set은 중복된 요소를 허용하지 않으므로, 같은 클래스의 인스턴스를 여러 번 추가해도 하나만 유지 * 그럼 중복해서 담기지 않고 싱글톤으로 담긴다. (컨트롤러는 주로 싱글톤으로 사용함) * @Controller를 전부 set에 담을거라는 소리 * ArrayList에 저장해도 상관은 없지만 set에 저장하는게 안전하다 * 패키지를 분석하여 UserController 클래스를 찾고, 해당 클래스의 인스턴스를 Set에 담는다. 이렇게 하면 Set에는 UserController 클래스의 인스턴스가 하나만 담기게 됨.
 

[ ClassLoader ]

// 이 코드(ClassLoader)를 통해서 패키지를 분석할 수 있다. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // ex03 패키지에 가서 @Controller 있나... 하고 보는 것임 URL packageUrl = classLoader.getResource("ex03");
ClassLoader를 사용하여 "ex03"이라는 패키지에 대한 URL을 가져오고 있는 것 * 코드는 언어마다 다르니 코드를 분석할 생각하지 말고, 어떤 기능을 하는지 흐름을 봐라.
notion image
toURI() try-catch 해야하는데.. 걍 메인에다 던질게요
notion image

[ OS는 파일과 디렉토리 모두를 파일로 간주 ]

운영 체제 관점에서, 파일 시스템에서 파일과 디렉토리 모두를 파일로 간주 (디렉토리도 어떤 정보를 담고 있음 = 파일 시스템)
 

[ 클래스 로더를 통해 프로젝트의 루트 위치를 찾을 수 있다 ]

클래스 로더가 Maven 또는 Gradle 프로젝트의 경우, 소스 코드는 "src/main/java" 폴더에 위치. 즉, 클래스 로더가 "src/main/java" 폴더에 들어가서 클래스 파일을 찾아준다. (실제로는 컴파일된 out 폴더에 들어가지만... 이렇게 이해해도 ㄱㅊ)
그럼, src/main/java" 폴더에 들어가 getResoursce 메서드를 이용해 클래스 경로를 기준으로 'ex03' 이라는 애를 찾을 것 `File dir = new File(packageUrl.toURI());` 애가 ex03이란 말임. `File ex03 = new File(packageUrl.toURI());` 이해하기 쉽게 이렇게 바꿔줌 ㅎㅎ 이제 ex03이 들고 있는 모든 파일들을 다 가져와서 for문을 돈다. 파일이 4개 있으니 4바퀴 돈다 (만약, ex03안에 파일이 5개 있으면 5개 돈다. 다 파일로 돈다는 뜻)
notion image
네모 친거 말고... for문 돌린걸 봐...

[ 이 경우엔 5개 ]

notion image
이러면 몇개냐? 5개다. ex03의 직접적인(?) 경로에 있는 애만 본다. 디렉토리(패키지)면 hello도 따로 지정을 해서 막 해줘야함.. 근데 이러면 너무 어렵잖아 지금은 안 해
notion image
여기 보이는 것처럼 확장자가 .class가 아니면 폴더라는 뜻… hello 이렇게만 나오잖아? 그럼 .class가 아니니까… 그 안에 들어가서 또 분석을 해줘야한다고ㅠㅠ

여기까지 코드

package ex03; import java.io.File; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; public class App { public static void findUri(UserController con, String path) { //UserController의 클래스를 들고와서 거기 안에 있는 메서드 다 들고와 Method[] methods = con.getClass().getDeclaredMethods(); for (Method method : methods) { RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); if (rm == null) continue; if (rm.uri().equals(path)) { try { method.invoke(con); // =con.login()이랑 같은 코드임 break; } catch (Exception e) { e.printStackTrace(); } } } } public static void main(String[] args) throws URISyntaxException { // 이 코드(ClassLoader)를 통해서 패키지를 분석할 수 있다. ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // ex03 패키지에 가서 @Controll잇나... 하고 보는 것임 URL packageUrl = classLoader.getResource("ex03"); File ex03 = new File(packageUrl.toURI()); for (File file : ex03.listFiles()) { System.out.println(file.getName()); } //findUri(new UserController(), "/login"); } }
notion image

[ Set<Object> ]

notion image
이 리플렉션을 구현하는 사람 입장에서는 뭐가 들어올지 모르니까 (제네릭으로 설계를 못하니까) Object 타입을 늫는다. 사용할 때 다운 캐스팅해서 써야겠죠?
 

 
notion image
notion image
notion image
이 이름(className)으로 new하려고 이름을 적어줌. 그래서 저 이상한 짓을 한 거다 이상한 짓 : "ex03" + "." + file.getName().replace(".class", "");
💡
문자열로도 리플렉션이 가능 (밑에 설명 ^^) 문자열로 리플렉션을 실행할 수 있는 것은 클래스 로더의 덕분
 

 
notion image
없는 클래스 명을 적을 수 있으니 익셉션이 뜸
notion image
main에 묶어줌
 

문자열로도 리플렉션 실행 됨

package ex04; public class Dog { public String name = "강아지"; public Dog() { System.out.println("강아지 객체 만들어짐"); } }
package ex04; public class DogApp { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { String className = "ex04.Dog"; Class cls = Class.forName(className); Object ob = cls.newInstance(); Dog d = (Dog) ob; //이렇게 다운캐스팅 해라. System.out.println(d.name); } }
notion image
일반적으로 클래스를 사용할 때는 해당 클래스의 이름을 알고 있어야 한다. 그리고 그 클래스의 이름을 사용하여 new 키워드를 통해 인스턴스를 생성할 수 있다. 그러나 때로는 프로그램이 런타임 중에 어떤 클래스를 사용해야 할지 미리 알 수 없는 경우가 있다 이럴 때 리플렉션을 사용하여 문자열로 클래스 이름을 지정하고, 해당 클래스를 동적으로 로드해서 인스턴스를 생성하고 사용할 수 있다.
💡
장점 : 클래스 이름을 몰라도 new 띄워서 사용할 수 있다!
 

 
notion image
이거… dog.class 뭐 이런식으로 하면 new를 못하고 클래스 로더가 못읽고??
 

이 녀석의 역할… 정리를 포기하겠다

notion image
@어노테이션 찾는 코드... 라는 것만 알자. 지금 짤 일 없다.
notion image
notion image
근데..? 갑자기 List로 바꿔줌
notion image
 

신입 때는 이렇게 할 필요없고 @어노테이션 이거만 쓸 수 있음 된다 깃발만 잘 꼽고 다니면 저 APP은 언젠가 내가 만들 날이 와요..

package ex03; import java.io.File; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; public class App { //그 /login 이런걸 찾아주는 코드 public static void findUri(List<Object> instances, String path){ for (Object instance : instances){ Method[] methods = instance.getClass().getDeclaredMethods(); for(Method method : methods){ RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); if(rm == null) continue; if(rm.uri().equals(path)){ try { method.invoke(instance); // con.login(); break; } catch (Exception e) { e.printStackTrace(); } } } } } // 클래스를 찾아주는 코드 public static List<Object> componentScan(String pkg) throws URISyntaxException, ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); URL packageUrl = classLoader.getResource(pkg); File ex03 = new File(packageUrl.toURI()); List<Object> instances = new ArrayList<>(); for (File file : ex03.listFiles()){ //System.out.println(file.getName()); if(file.getName().endsWith(".class")){ String className = pkg + "." + file.getName().replace(".class", ""); //System.out.println(className); Class cls = Class.forName(className); if(cls.isAnnotationPresent(Controller.class)){ Object instance = cls.newInstance(); instances.add(instance); // UserController, BoardController } } } return instances; } public static void main(String[] args) throws URISyntaxException, ClassNotFoundException, InstantiationException, IllegalAccessException { List<Object> instances = componentScan("ex03"); findUri(instances, "/login"); } }
package ex03; import ex02.RequestMapping; @Controller public class BoardController { @RequestMapping(uri = "/write") public void write() { System.out.println("글쓰기 호출됨"); } }
notion image

예제 3 - 전체 코드

[ UserController ]

package ex03; @Controller public class UserController { @RequestMapping(uri = "/login") public void login() { System.out.println("로그인 호출됨"); } @RequestMapping(uri = "/join") public void join() { System.out.println("회원가입 호출됨"); } // 새로 이런 메서드를 만들었는데 서로 전화통화해서 이거 만들었어요! 하기 전에는 알 수 있는 방법이 없다. @RequestMapping(uri = "/userInfo") public void userInfo() { System.out.println("유저 정보 보기"); } @RequestMapping(uri = "/update-password") public void updatePassword(){ System.out.println("패스워드 수정하기"); } }

[ BoardController ]

package ex03; @Controller public class BoardController { public void write(){ System.out.println("글쓰기 호출됨"); } }

[ @Controller ]

package ex03; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) // 클래스 위에 붙일 때는 TYPE으로 한다. public @interface Controller { // Controller는 클라이언트의 요청을 처리하고, 응답을 생성하는 역할을 담당한다. }

[ @RequestMapping ]

package ex03; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) // 어노테이션은 원래 컴파일시점에 발동되는데 런타임으로 해놓으면 런타임 시점에 발동됨 @Target(ElementType.METHOD) public @interface RequestMapping { // RequestMapping은 컨트롤러의 메서드에 적용되어 해당 메서드가 특정 URL 요청과 매핑(연결)되도록 설정한다. 즉, 클라이언트의 특정 URL 요청이 들어왔을 때, 해당 요청을 처리하기 위한 메서드를 지정하는 역할을 한다. String uri(); // identify /account/1 의 1 이런 것! }

[ App ]

package ex03; import java.io.File; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; // ex02 리팩토링 public class App { public static void findUri(List<Object> instances, String path){ for (Object instance : instances){ Method[] methods = instance.getClass().getDeclaredMethods(); for(Method method : methods){ RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); // 이 메서드에 어떤 메서드가 있지? 이런 말!! if(rm == null) continue; if(rm.uri().equals(path)){ try { method.invoke(instance); // con.login(); break; } catch (Exception e) { e.printStackTrace(); } } } } } public static List<Object> componentScan(String pkg) throws URISyntaxException, ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 클래스로더: 패키지를 분석할 수 있다. URL packageUrl = classLoader.getResource(pkg); // ex03에 있는 어노테이션을 다 분석한다. File ex03 = new File(packageUrl.toURI()); List<Object> instances = new ArrayList<>(); // 뭐가 들어갈지 모르기 때문에 오브젝트에 담는다. for (File file : ex03.listFiles()){ // 오에스의 관점에서는 디렉터리도 뭐도 다 파일로 보인다. 그래서 디렉터리를 읽을 때 그냥 파일로 읽는다. // ex03안에 4개의 파일이 있기 때문에 이 포문은 내 바퀴 돈다. 네 바퀴를 돌면서 안에 @컨트롤러가 있나 확인하고 셋 자료 형에 담아놓는다. //System.out.println(file.getName()); if(file.getName().endsWith(".class")){ // 패키지는 탐색할 필요가 없으니 .class가 뒤에 붙는 애만 가지고 하겠다. String className = pkg + "." + file.getName().replace(".class", ""); // 이렇게하면 .class가 공백으로 처리됨 // 이렇게 이름을 바꾼 이유!!! 이렇게 해야 이 바뀐 이름으로 뉴를 할 수 있음. //System.out.println(className); Class cls = Class.forName(className); // 없는 클래스 이름이 나오면 오류날 수 있으니까 예외처리를 해둔다. if(cls.isAnnotationPresent(Controller.class)){ Object instance = cls.newInstance(); // 여기 들어올 수 있는 것은 유저컨트롤러뿐이야 지금은. @컨트롤러가 거기에만 붙어서. instances.add(instance); // UserController, BoardController } } } return instances; } public static void main(String[] args) throws URISyntaxException, ClassNotFoundException, InstantiationException, IllegalAccessException { // 패키지를 분석해서 골뱅이 컨트롤러라는 것이 붙은 애들을 다 뉴해서 셋 자료형에 담음. 그러면 중복이 없는 싱글톤으로 담김. 어레이리스트에 담아도 되지만 셋에 담는 것이 안전함 // 클래스로더는 아웃 폴더로 가는데 그냥 메인.자바로 간다고 봐도 무방 List<Object> instances = componentScan("ex03"); findUri(instances, "/login"); // UserController 외에 다른 컨트롤러에도 사용될 수 있게 만들자!!! } }
 
 
Share article

codingb