
Reflection
: 실행 중에 자기 자신의 구조를 검사하고 조작할 수 있는 능력
예시) 내가 무슨 메서드를 만들지 라이브러리를 만드는 사람은 알 수 없으나
내가 메서드를 만들면 동작시킬 수 있음

정원사가 나무를 관리함
나무는 정상적인 나무도 있고,
썩어서 보수를 해줘야 하는 나무도 있음
리플렉션은 모든 나무를 하나하나 관리
그래서 리플렉션을 통한 접근은 느림
장점 : 구체적인 클래스를 알지 못해도 동적으로 클래스를 만들어 의존 관계를 만들 수 있음.
단점 : 분석을 많이 해서 속도가 느림 / 기존 프로그램이 더 빠름
private 데이터도 접근 가능해 캡슐화 어려움
런타임 단계에서 에러가 발생하기 때문에 디버깅 어려움
미리 코딩할 수 없음

리플렉션을 효율적으로 사용하기 위해 어노테이션(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();
}
}
}



검사하는게 리플렉션
어노테이션은 검사를 효율적으로 하게 만들어줌
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();
}

리플렉션으로 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();
}
}

모든 컨트롤러에 있는 어노테이션 분석을 위해
패키지를 리플렉션해서 컨트롤러를 다 찾아낼 것임
컨포넌트 스캔 : 패키지를 분석해서 찾아서 메모리에 띄울 것을 뉴해서 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");
}
}

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");
}
}

문자열로도 뉴할 수 있음→ 리플렉션에 필요함
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);
}
}

어떤 클래스를 만들지 모를 때 이렇게 뉴 해야함
그럴 때 리플렉션으로 클래스 이름을 분석해서 뉴할 것
이것을 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 적용 안될 때

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);
}
}

Share article