Reflection

[JDBC] Reflection 개념 작동원리 이해
Jan 23, 2024
Reflection
단점 : 다 분석하기 때문에 많이 쓰면 시간이 너무 오래 걸려 느리다.
→ 적재적소에 쓰는것이 중요함.
어노테이션을 사용하면 리플렉션을 효율적으로 사용가능 → 리플렉션을 사용할지 말지 결정 하기 때문에 효율적

예제

예제를 통해 리플렉션의 사용 전 후의 차이를 비교
코드를 이해하지말고 어떻게 작동하는지 작동 원리를 이해하자

ex01

생성

Java Class : App, UserController
notion image
App
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.join(); } } }
UserController
package ex01; public class UserController { // /login public void login(){ System.out.println("로그인 호출됨"); } // /join public void join(){ System.out.println("회원가입 호출됨"); } public void userinfo(){ System.out.println("유저정보 보기"); } }
결과
notion image
App main에서 path값에 따라 UserController의 메서드 실행
UserController에 메서드를 하나 더 생성하면 App의 main 코드도 수정 하여야 한다. → ex02

ex02

생성

Java Class : App, UserController
Java Annotation : RequestMapping
notion image
App
package ex02; import java.lang.reflect.Method; public class App { public static void main(String[] args) { String path = "/updatepassword"; 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)){ try { method.invoke(con); // con.login() break; } catch (Exception e) { e.printStackTrace(); } } } } }
App의 코드를 수정 하지 않아도 됨(path는 원래 입력받는 값이라 App의 코드에 포함 x)
UserController
package ex02; public class UserController { // /login @RequestMapping(uri = "/login") public void login() { System.out.println("로그인 호출됨"); } // /join @RequestMapping(uri = "/join") public void join() { System.out.println("회원가입 호출됨"); } @RequestMapping(uri = "/userinfo") public void userinfo() { System.out.println("유저정보 보기"); } @RequestMapping(uri = "/updatepassword") public void updatePassword() { System.out.println("패스워드 수정하기"); } }
UserController에 원하는 메서드들을 추가가능
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(); }
RequestMapping 이라는 어노테이션을 만들어서 UserController에 있는 메서드들을 원하는 것만 분석하여 효율적으로 리플렉션 사용 가능.
 
하지만 위 코드는 컨트롤러가 늘어나면 해결이 불가능 → ex03

ex03

@Controller를 List에 저장 안전하게 하려면 Set(같은 타입 중복이 안됨)에다가 저장

생성

Java Class : App, UserController, BoardController
Java Annotation : RequestMapping, Controller
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 { // RequestMapping 어노테이션이 붙어있는 것을 확인 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(); } } } } } // Controller 어노테이션이 붙어있는거 확인 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, "/write"); } }
 
어노테이션이 있는지 확인하는 코드 생성(코드 생성할줄 몰라도 됨)
UserController
package ex03; @Controller public class UserController { // /login @RequestMapping(uri = "/login") public void login() { System.out.println("로그인 호출됨"); } // /join @RequestMapping(uri = "/join") public void join() { System.out.println("회원가입 호출됨"); } @RequestMapping(uri = "/userinfo") public void userinfo() { System.out.println("유저정보 보기"); } @RequestMapping(uri = "/updatepassword") public void updatePassword() { System.out.println("패스워드 수정하기"); } }
BoardController
package ex03; @Controller public class BoardController { @RequestMapping(uri = "/write") 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) // 클래스 위에 public @interface Controller { }
클래스를 찾아주는 어노테이션 → componentScan
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 { String uri(); }
메서드를 찾아주는 어노테이션 → findUri
 
이렇게 하면 컨트롤러가 여러개여도 상관없음.

Main → Dispatcher → Controller → Dao → Database
Main은 Dispatcher 호출, Dispatcher는 Controller 호출….. Dao는 Database 호출 순으로 됨.
나누는 이유는 각각의 책임 분리를 위해 → SRP(단일 책임 원칙)
 
Share article
RSSPowered by inblog