Reflection

Jan 23, 2024
Reflection

Reflection

: 실행 중에 자기 자신의 구조를 검사하고 조작할 수 있는 능력
 
예시) 내가 무슨 메서드를 만들지 라이브러리를 만드는 사람은 알 수 없으나
내가 메서드를 만들면 동작시킬 수 있음
notion image
정원사가 나무를 관리함
나무는 정상적인 나무도 있고,
썩어서 보수를 해줘야 하는 나무도 있음
리플렉션은 모든 나무를 하나하나 관리
그래서 리플렉션을 통한 접근은 느림
장점 : 구체적인 클래스를 알지 못해도 동적으로 클래스를 만들어 의존 관계를 만들 수 있음.
단점 : 분석을 많이 해서 속도가 느림 / 기존 프로그램이 더 빠름
private 데이터도 접근 가능해 캡슐화 어려움
런타임 단계에서 에러가 발생하기 때문에 디버깅 어려움
미리 코딩할 수 없음
 
notion image
리플렉션을 효율적으로 사용하기 위해 어노테이션(Annotation)을 사용한다. 어노테이션은 깃발을 꽂는다고 생각하면 된다. 내가 관리를 원하는 메서드에 깃발을 꽂으면 그 메서드만 조작할 수 있다.
 
변할 때 서로의 개발자가 다를 때) 늘릴 때 마다 커뮤니케이션 해야 함
 
package ex01; public class App { public static void main(String[] args) { String path = "/login"; UserController con = new UserController(); if(path.equals("/login")) { con.login(); } else if (path.equals("/join")) { con.login(); } } }
notion image
notion image
notion image
 
검사하는게 리플렉션
어노테이션은 검사를 효율적으로 하게 만들어줌
 
package ex02; import java.lang.reflect.Method; public class App { public static void main(String[] args) { String path = "/login"; UserController con = new UserController(); Method[] methods = con.getClass().getDeclaredMethods(); System.out.println(methods.length); for(Method method : methods){ System.out.println(method.getName()); } } }
package ex02; public class UserController { public void login(){ System.out.println("로그인 호출됨"); } public void join(){ System.out.println("회원가입 호출됨"); } public void userinfo(){ System.out.println("유저정보 보기"); } }
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 { // 어노테이션 // 인터페이스앞에 @interface annotation이다 // 발동시점을 정해야함 힌트를 컴파일시점에서 보고 작동함 // 깃발을 꽂음 String uri(); }
notion image
 
리플렉션으로 Annotation을 분석하는 것
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 = "/login"; UserController con = new UserController(); Method[] methods = con.getClass().getDeclaredMethods(); System.out.println(methods.length); for (Method method : methods) { System.out.println(method.getName()); RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class); if (rm == null) continue; if (rm.uri().equals(path)) {// = con.login(); 과 같은 것 try { method.invoke(con); break; } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (Exception e) { e.printStackTrace(); } } } } }
도메인마다 컨트롤러를 만듦
 
메서드화
package ex03; import java.lang.reflect.Method; public class App { public static void findUri(UserController con, String path){ 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); break; } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (Exception e) { e.printStackTrace(); } } } } public static void main(String[] args) { String path = "/update-Password"; findUri(new UserController(), "/login"); UserController con = new UserController(); } }
notion image
모든 컨트롤러에 있는 어노테이션 분석을 위해
패키지를 리플렉션해서 컨트롤러를 다 찾아낼 것임
 
컨포넌트 스캔 : 패키지를 분석해서 찾아서 메모리에 띄울 것을 뉴해서 set 자료형에 담음 → 중복 안됨/ 싱글톤에 담음
절대 중복될 수 없음
 
classLoader를 통해서 메인의 자바에 접근 할 수 있음
실제 접근하는 것 classis니까 java로 생각해도 됨
찾아서 그 안에 있는 것들 중에 ex03이라는 패키지의 위치를 찾음
ex03을 파일 객체로 읽음 → 자바 객체로 들어와있음 /폴더같은 것
ex03이 가지고 있는 모든 파일들만큼 for문이 돌아감
폴더도 파일로 봄
 
자기 경로에 있는 것만 보기에 그 폴더 안에 있는 파일까지 보지 않음
 
있으면 set 자료형에 담음
폴더만 다시 리스트 파일즈 해서 들어가서 다시 있는지 확인해서 있으면 set 자료형에 담음
 
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) { 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); break; } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (Exception e) { e.printStackTrace(); } } } } public static void main(String[] args) throws URISyntaxException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 패키지 분석 URL packasgeUrl = classLoader.getResource("ex03"); // 패키지 분석 File ex03 = new File(packasgeUrl.toURI()); for(File file : ex03.listFiles()){ // ex03이 가지고 있는 모든 파일들만큼 for문이 돌아감 System.out.println(file.getName()); } //findUri(new UserController(), "/login"); } }
notion image
package ex03; import java.io.File; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.util.HashSet; import java.util.Set; public class App { public static void findUri(UserController con, String path) { 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); break; } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (Exception e) { e.printStackTrace(); } } } } public static void main(String[] args) throws URISyntaxException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 패키지 분석 URL packasgeUrl = classLoader.getResource("ex03"); // 패키지 분석 File ex03 = new File(packasgeUrl.toURI()); Set<Object> instances = new HashSet<>(); for(File file : ex03.listFiles()){ // ex03이 가지고 있는 모든 파일들만큼 for문이 돌아감 //System.out.println(file.getName()); if (file.getName().endsWith(".class")){ String className = "ex03" + "." + file.getName().replace(".class", ""); System.out.println(className); } } //findUri(new UserController(), "/login"); } }
notion image
 
문자열로도 뉴할 수 있음→ 리플렉션에 필요함
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 InstantiationException, IllegalAccessException, ClassNotFoundException { String className = "ex04.Dog"; Class cls = Class.forName(className); Object ob = cls.newInstance(); Dog d = (Dog) ob; System.out.println(d.name); } }
notion image
어떤 클래스를 만들지 모를 때 이렇게 뉴 해야함
그럴 때 리플렉션으로 클래스 이름을 분석해서 뉴할 것
이것을 set자료형에 담을 것
 
ex03" + "." + file.getName() // 뉴하기 위해서
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 { 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; @Controller public class BoardController { public void write(){ System.out.println("글쓰기 호출됨"); } }
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) //런타임 실행시 -> 깃발을 꽂을때 하는 행위 = findUri @Target(ElementType.TYPE) // 클래스 위에 붙일 때 public @interface 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.METHOD) public @interface RequestMapping { // 어노테이션 //인터페이스앞에 @interface annotation이다 //발동시점을 정해야함 힌트를 컴파일시점에서 보고 작동함 String uri(); }
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("비밀번호 수정하기"); } }
lombok 적용 안될 때
notion image
package controller; import dao.BankDAO; import lombok.AllArgsConstructor; /** * 책임 : 유효성검사(바디데이터), 파싱(바디데이터), 적절한 DAO 찾기 */ @AllArgsConstructor public class BankController { private BankDAO dao; @RequestMapping(uri = "/insert") public void insert(){ // 1. 파싱 // 2. 유효성검사 // 3. dao 찾기 System.out.println("controller : insert"); dao.insert("1234", 1000); } @RequestMapping(uri = "/delete") public void delete(){ System.out.println("controller : delete"); dao.deleteByNumber(1); } @RequestMapping(uri = "/update") public void update(){ System.out.println("controller : update"); dao.updateByNumber(1000, 1); } @RequestMapping(uri = "/selectOne") public void selectOne(){ System.out.println("controller : selectOne"); dao.selectByNumber(1); } @RequestMapping(uri = "/selectAll") public void selectAll(){ System.out.println("controller : selectAll"); dao.selectAll(); } @RequestMapping(uri = "/withdraw") public void withdraw(){ System.out.println("controller : withdraw"); dao.updateByNumber(1000, 1); dao.updateByNumber(-1000, 2); } @RequestMapping(uri = "/deposit") public void deposit(){ System.out.println("controller : deposit"); dao.updateByNumber(1000, 1); } }
import controller.BankController; import controller.RequestMapping; import lombok.AllArgsConstructor; import java.lang.reflect.Method; /** * 책임 : 라우팅 */ @AllArgsConstructor public class Dispatcher { private BankController con; public void route(String path){ 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); break; } catch (Exception e) { e.printStackTrace(); } } } } }
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(); }
import controller.BankController; import dao.BankDAO; public class BankApp { public static void main(String[] args) { String uri = "/withdraw"; BankDAO dao = new BankDAO(); BankController con = new BankController(dao); Dispatcher dis = new Dispatcher(con); dis.route(uri); } }
notion image
Share article

vosw1