Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[Java] 예제 소스 정리 - Reflect 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/DBMS, SQL, JDBC, Servlet

[Java] 예제 소스 정리 - Reflect

끈기JK 2023. 2. 27. 20:58

com.eomcs.reflect

 

 

예제 소스 정리

 

 

reflect

 

 

com.eomcs.reflect.ex01

 

 

Reflection API : 클래스 로딩
package com.eomcs.reflect.ex01;

class A {
  static int i;

  static void m() {
    i = 100;
    System.out.println(i);
  }

  static {
    System.out.println("A 클래스 로딩!");
  }

  public A() {
    System.out.println("A() 생성자 호출!");
  }
}


public class Exam0110 {

  public static void main(String[] args) throws Exception {
    // 클래스 로딩
    // => 클래스가 로딩되어 있지 않을 때 다음을 수행하면 클래스가 로딩된다.
    // 1) 클래스의 스태틱 멤버(변수,메서드)를 사용할 때
    // 2) new 명령을 사용하여 인스턴스를 생성하려 할 때
    // 3) Class.forName()을 이용하여 임의로 클래스를 로딩할 때
    //
    // 클래스 로딩 과정에서 하는 일
    // => 스태틱 변수를 준비한다.
    // => 스태틱 블록을 실행한다.

    //    A.i = 100; // 클래스 로딩 확인!
    //    A.m(); // 클래스 로딩 확인!
    //    new A(); // 클래스 로딩 확인!
    //    new A(); // 클래스는 중복으로 로딩되지 않는다.

    //    Class.forName("com.eomcs.reflect.ex01.A");
    // 파라미터로 패키지명을 포함한 전체 클래스 이름을 지정해야 한다.
    // 패키지명을 포함한 전체 클래스 이름
    // = fully qualified class name
    // = FQName
    // = QName

    //    A obj = null; // 레퍼런스 선언은 클래스 로딩과 상관 없다.
    //    A[] arr; // 배열 레퍼런스 선언도 클래스 로딩과 상관 없다.
    //    arr = new A[100]; // 레퍼런스 배열도 마찬가지로 클래스 로딩과 상관없다.

    System.out.println("실행 했음!");
  }

}

 

 

Reflection API : 중첩 클래스 로딩
package com.eomcs.reflect.ex01;

class B {
  static {
    System.out.println("B 클래스 로딩됨!");
  }

  static class C {
    static int s_var = 100;
    int i_var = 200;

    static void s_m() {}

    void i_m() {}

    static {
      System.out.println("B의 중첩클래스 C 로딩됨!");
    }
  }
}


public class Exam0120 {

  public static void main(String[] args) throws Exception {
    //    Class.forName("com.eomcs.reflect.ex01.B");
    // => 바깥 클래스가 로딩되었다고 중첩 클래스가 자동으로 로딩되는 것은 아니다.

    //    Class.forName("com.eomcs.reflect.ex01.B$C");
    // => 중첩 클래스는 "바깥클래스명$중첩클래스명" 형식의 이름을 갖는다.
    // => 스태틱 중첩 클래스를 로딩할 때는 바깥 클래스를 로딩하지는 않는다.
    //    스태틱 중첩 클래스 입장에서는 바깥 클래스가 패키지의 역할을 할 뿐이다.
    // => 바깥 클래스 파일이 존재하지 않더라도 실행 오류가 발생하지 않는다.
    // 
    // 주의! 
    // => 자바 코드로 중첩 클래스를 표현할 때는
    //    클래스 이름에 $ 를 붙이지 않고 . 을 붙인다.
    //    com.eomcs.reflect.ex01.B.C obj;
    //    obj = new com.eomcs.reflect.ex01.B.C();

    System.out.println("실행 완료!");
  }

}

 

 

Reflection API : 클래스 정보를 얻는 방식 - forName()과 "class"라는 스태틱 변수
package com.eomcs.reflect.ex01;

import java.io.File;

public class Exam0210 {

  static class A {
    static int s_var = 100;
    int i_var = 200;

    static void s_m() {}

    void i_m() {}

    static {
      System.out.println("A 클래스 로딩!");
    }
  }

  public static void main(String[] args) throws Exception {
    // 다음은 클래스를 로딩한 후 스태틱 필드를 생성하고 스태틱 블록을 실행한다.
    //    Class clazz = Class.forName("com.eomcs.reflect.ex01.Exam03$A");
    System.out.println("-----------------------------");

    // 자바의 모든 클래스는 "class"라는 특별한 스태틱 변수를 갖고 있다.
    // "class" 변수에는 해당 클래스의 정보를 담은 Class 객체의 주소가 저장되어 있다.
    Class clazz1 = A.class;
    // 하지만 이 방식은 static {} 블록을 실행하지 않는다.
    // 물론 스태틱 멤버를 사용하는 최초의 순간에는 static 블록이 실행될 것이기 때문에
    // "클래스 로딩 후 스태틱 블록 실행" 명제는 지켜진다.

    // 클래스 정보를 얻기 위해 A.class 처럼 class 라는 스태틱 변수를 사용하는 것이 
    // forName()을 호출하는 것 보다 안 좋은 이유는?
    // - forName()의 파라미터는 문자열이다.
    // - 즉 외부에서 문자열을 입력 받아 해당 클래스를 임의로 로딩할 수 있지만,
    //   "class"라는 스태틱 변수를 사용하는 것은
    //   자바 소스 안에 명확히 해당 클래스를 지정(하드코딩)해야 한다.
    // - 이런 방식은 다른 클래스를 로딩하려면 다시 코드를 변경해야 하는 문제가 있다.

    Class clazz2 = String.class;
    Class clazz3 = System.class;
    Class clazz4 = File.class;
    Class clazz5 = int.class;

    System.out.println("실행 완료!");
  }

}

 

 

Reflection API : forName() 활용
package com.eomcs.reflect.ex01;

import java.util.Scanner;

class X {
  static {
    System.out.println("X 클래스 로딩");
  }
}


class Y {
  static {
    System.out.println("Y 클래스 로딩");
  }
}


class Z {
  static {
    System.out.println("Z 클래스 로딩");
  }
}


public class Exam0220 {

  public static void main(String[] args) throws Exception {
    Scanner keyScan = new Scanner(System.in);
    System.out.println("로딩할 클래스명을 입력하시오? ");
    String className = keyScan.nextLine();
    keyScan.close();

    // forName()은
    // 이렇게 프로그램 아규먼트나 키보드 입력을 통해
    // 클래스 이름을 입력 받아서 로딩할 수 있다.
    Class.forName(className);

    // 그러나 다음과 같이 "class" 변수를 사용하게 되면
    // 코드로 이름을 고정하기 때문에 임의의 클래스를 로딩할 수 없다.
    // 다른 클래스를 가져오려면 소스 코드를 변경해야 한다.
    Class clazz = X.class;
  }

}

 

 

Reflection API : 클래스 로딩과 인스턴스 생성
package com.eomcs.reflect.ex01;

public class Exam0310 {

  static class A {
    void m() {
      System.out.println("Hello!");
    }
  }

  public static void main(String[] args) throws Exception {
    // 1) 일반적인 인스턴스 생성
    //    A obj1 = new A();

    // 2) 클래스 정보를 가지고 인스턴스 생성
    // => 클래스 정보를 로딩한다.
    Class clazz = Class.forName("com.eomcs.reflect.ex01.Exam0310$A");

    // 타입(클래스) 정보만 있다면 인스턴스 생성할 수 있다.
    A obj = (A) clazz.newInstance();
    obj.m();

    // deprecated 메서드이다.
    // 자바는 생성자를 이용하여 인스턴스를 생성할 것을 권고하고 있다.
  }

}

 

 

com.eomcs.reflect.ex02

 

 

클래스 정보 추출 - 클래스 이름 알아내기
package com.eomcs.reflect.ex02;

public class Exam0110 {

  static class A {}

  static Object obj = new Object() {
    @Override
    public String toString() {
      return "익명 클래스";
    }
  };

  public static void main(String[] args) throws Exception {
    //1) 패키지 멤버 클래스
    Class<?> clazz = Class.forName("java.lang.String");

    // '클래스의 타입 정보를 담은 객체'를 통해 클래스 정보를 추출할 수 있다.
    System.out.println(clazz.getSimpleName()); // String
    System.out.println(clazz.getName()); // java.lang.String
    System.out.println(clazz.getCanonicalName()); // java.lang.String
    System.out.println(clazz.getTypeName()); // java.lang.String

    System.out.println("------------------------------------------");

    //2) 중첩 클래스
    Class<?> clazz2 = Class.forName("com.eomcs.reflect.ex02.Exam01$A");

    // 클래스의 타입 객체를 통해 클래스 정보를 추출할 수 있다.
    System.out.println(clazz2.getSimpleName()); // A
    System.out.println(clazz2.getName()); // com.eomcs.reflect.ex02.Exam01$A
    System.out.println(clazz2.getCanonicalName()); // com.eomcs.reflect.ex02.Exam01.A
    System.out.println(clazz2.getTypeName()); // com.eomcs.reflect.ex02.Exam01$A

    System.out.println("------------------------------------------");

    //3) 익명 클래스
    Class<?> clazz3 = obj.getClass(); // 익명 클래스의 인스턴스로부터 클래스 정보 추출

    // 클래스의 타입 객체를 통해 클래스 정보를 추출할 수 있다.
    System.out.println(clazz3.getSimpleName()); //
    System.out.println(clazz3.getName()); // com.eomcs.reflect.ex02.Exam01$1
    System.out.println(clazz3.getCanonicalName()); // null
    System.out.println(clazz3.getTypeName()); // com.eomcs.reflect.ex02.Exam01$1
  }

}

 

 

클래스 정보 추출 - 배열의 이름
package com.eomcs.reflect.ex02;

import java.sql.Date;
import java.util.ArrayList;

public class Exam0120 {

  public static void main(String[] args) throws Exception {
    System.out.println(byte.class.getName());  // byte 타입
    System.out.println(byte[].class.getName()); // byte 배열 타입
    System.out.println("---------------------------");

    System.out.println(short.class.getName());
    System.out.println(short[].class.getName());
    System.out.println("---------------------------");

    System.out.println(int.class.getName());
    System.out.println(int[].class.getName());
    System.out.println("---------------------------");

    System.out.println(long.class.getName());
    System.out.println(long[].class.getName());
    System.out.println("---------------------------");

    System.out.println(float.class.getName());
    System.out.println(float[].class.getName());
    System.out.println("---------------------------");

    System.out.println(double.class.getName());
    System.out.println(double[].class.getName());
    System.out.println("---------------------------");

    System.out.println(boolean.class.getName());
    System.out.println(boolean[].class.getName());
    System.out.println("---------------------------");

    System.out.println(char.class.getName());
    System.out.println(char[].class.getName());
    System.out.println("---------------------------");

    System.out.println(String.class.getName());
    System.out.println(String[].class.getName());
    System.out.println("---------------------------");

    System.out.println(Date.class.getName());
    System.out.println(Date[].class.getName());
    System.out.println("---------------------------");

    System.out.println(ArrayList.class.getName());
    System.out.println(ArrayList[].class.getName());
    System.out.println("---------------------------");

  }
}

결과

byte
[B
---------------------------
short
[S
---------------------------
int
[I
---------------------------
long
[J
---------------------------
float
[F
---------------------------
double
[D
---------------------------
boolean
[Z
---------------------------
char
[C
---------------------------
java.lang.String
[Ljava.lang.String;
---------------------------
java.sql.Date
[Ljava.sql.Date;
---------------------------
java.util.ArrayList
[Ljava.util.ArrayList;
---------------------------

 

 

클래스 정보 추출 - 배열 항목의 타입
package com.eomcs.reflect.ex02;

import java.sql.Date;

public class Exam0130 {

  public static void main(String[] args) throws Exception {
    // getComponentType()
    // - 배열의 항목에 대한 타입 정보를 리턴한다.
    // - 배열이 아니라면, 항목이 없기 때문에 리턴 값은 null이다.
    // 
    System.out.println(byte.class.getComponentType());
    System.out.println("---------------------------");

    System.out.println(byte.class.getName());
    System.out.println(byte[].class.getName());
    System.out.println(byte[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(short.class.getName());
    System.out.println(short[].class.getName());
    System.out.println(short[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(int.class.getName());
    System.out.println(int[].class.getName());
    System.out.println(int[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(long.class.getName());
    System.out.println(long[].class.getName());
    System.out.println(long[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(float.class.getName());
    System.out.println(float[].class.getName());
    System.out.println(float[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(double.class.getName());
    System.out.println(double[].class.getName());
    System.out.println(double[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(boolean.class.getName());
    System.out.println(boolean[].class.getName());
    System.out.println(boolean[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(char.class.getName());
    System.out.println(char[].class.getName());
    System.out.println(char[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(String.class.getName());
    System.out.println(String[].class.getName());
    System.out.println(String[].class.getComponentType().getName());
    System.out.println("---------------------------");

    System.out.println(Date.class.getName());
    System.out.println(Date[].class.getName());
    System.out.println(Date[].class.getComponentType().getName());
    System.out.println("---------------------------");
  }

}

결과

null
---------------------------
byte
[B
byte
---------------------------
short
[S
short
---------------------------
int
[I
int
---------------------------
long
[J
long
---------------------------
float
[F
float
---------------------------
double
[D
double
---------------------------
boolean
[Z
boolean
---------------------------
char
[C
char
---------------------------
java.lang.String
[Ljava.lang.String;
java.lang.String
---------------------------
java.sql.Date
[Ljava.sql.Date;
java.sql.Date
---------------------------

 

 

클래스 정보 추출 - 컬렉션의 값 타입
package com.eomcs.reflect.ex02;

import java.util.ArrayList;

public class Exam0140 {

  public static void main(String[] args) throws Exception {
    ArrayList<Object> values = new ArrayList<>();
    values.add(100);
    values.add(100L);
    values.add(3.14f);
    values.add(314.55);
    values.add(true);
    values.add('A');
    values.add("Hello");
    values.add(new int[] {100, 200, 300});
    values.add(new String[] {"aaa", "bbb", "ccc"});

    for (Object value : values) {
      printTypeInfo(value.getClass());
    }
  }

  private static void printTypeInfo(Class<?> type) {
    if (type.getName().startsWith("[")) {
      System.out.printf("=> %s[]\n", type.getComponentType().getName());
    } else {
      System.out.printf("=> %s\n", type.getName());
    }
  }

}

결과

=> java.lang.Integer
=> java.lang.Long
=> java.lang.Float
=> java.lang.Double
=> java.lang.Boolean
=> java.lang.Character
=> java.lang.String
=> int[]
=> java.lang.String[]

 

 

클래스 정보 추출 - 클래스의 수퍼 클래스 정보 알아내기
package com.eomcs.reflect.ex02;

public class Exam0210 {
  static class A {
  }
  static class B extends A {
  }
  static class C extends B {
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Class.forName("com.eomcs.reflect.ex02.Exam0210$C");

    // 수퍼 클래스의 타입을 알아내기
    Class<?> superClazz = clazz.getSuperclass();
    System.out.println(superClazz.getName());
    System.out.println(superClazz.getSuperclass().getName());
  }
}

결과

com.eomcs.reflect.ex02.Exam0210$B
com.eomcs.reflect.ex02.Exam0210$A

 

 

클래스 정보 추출 - 클래스의 중첩 클래스 정보 알아내기
package com.eomcs.reflect.ex02;

public class Exam0310 {

  static class A {

    static class B {
    } // static nested class

    class C {
    } // non-static nested class == inner class

    public void m() {
      class D {
      } // local class
    }

    public void m2() {
      Object obj = new Object() {}; // anonymous class
    }

    public static class E {
    }

    public class F {
    }

    public interface X {
    }
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Class.forName("com.eomcs.reflect.ex02.Exam0310$A");

    // public 으로 공개된 중첩 클래스 및 인터페이스 정보를 가져온다.
    Class<?>[] nestedList = clazz.getClasses();

    for (Class<?> nested : nestedList) {
      System.out.println(nested.getName());
    }

  }

}

결과

com.eomcs.reflect.ex02.Exam0310$A$E
com.eomcs.reflect.ex02.Exam0310$A$F
com.eomcs.reflect.ex02.Exam0310$A$X

 

 

클래스 정보 추출 - 클래스의 중첩 클래스 정보 알아내기 II
package com.eomcs.reflect.ex02;

public class Exam0320 {
  static class A {
    static class B {
    } // static nested class
    class C {
    } // non-static nested class == inner class

    public void m() {
      class D {
      } // local class
    }

    public void m2() {
      Object obj = new Object() {}; // anonymous class
    }

    public static class E {
    }
    public class F {
    }
    private class G {
    }
    protected class H {
    }
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Class.forName("com.eomcs.reflect.ex02.Exam0320$A");

    // 접근 범위에 상관 없이 모든 중첩 클래스 및 인터페이스 정보를 가져온다.
    // => 메서드 안에 정의된 로컬 클래스는 대상이 아니다.
    Class<?>[] nestedList = clazz.getDeclaredClasses();
    for (Class<?> nested : nestedList) {
      System.out.println(nested.getName());
    }

  }

}

결과

com.eomcs.reflect.ex02.Exam0320$A$B
com.eomcs.reflect.ex02.Exam0320$A$C
com.eomcs.reflect.ex02.Exam0320$A$E
com.eomcs.reflect.ex02.Exam0320$A$F
com.eomcs.reflect.ex02.Exam0320$A$G
com.eomcs.reflect.ex02.Exam0320$A$H

 

 

클래스 정보 추출 - 구현 인터페이스 정보 알아내기
package com.eomcs.reflect.ex02;

public class Exam0410 {

  static interface A {
  }

  static interface B {
  }

  static interface C {
  }

  static class D implements A, B, C {
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Class.forName("com.eomcs.reflect.ex02.Exam0410$D");

    // 해당 클래스가 구현한 인터페이스 정보를 가져온다.
    Class<?>[] list = clazz.getInterfaces();
    for (Class<?> c : list) {
      System.out.println(c.getName());
    }

  }

}

결과

com.eomcs.reflect.ex02.Exam0410$A
com.eomcs.reflect.ex02.Exam0410$B
com.eomcs.reflect.ex02.Exam0410$C

 

 

클래스 정보 추출 - 패키지 정보 알아내기
package com.eomcs.reflect.ex02;

public class Exam0510 {

  static interface A {
  }

  static interface B {
  }

  static interface C {
  }

  static class D implements A, B, C {
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Class.forName("com.eomcs.reflect.ex02.Exam0510$D");

    // 해당 클래스의 패키지 정보를 가져온다.
    Package p = clazz.getPackage();
    System.out.println(p.getName());
  }

}

결과

com.eomcs.reflect.ex02

 

 

com.eomcs.reflect.ex03

 

 

메서드 정보 추출 - getMethod()
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0110 {
  public static void m1() {}

  public void m2() {}

  protected void m3() {}

  void m4() {}

  private void m5() {}

  public static void main(String[] args) {
    Class<?> clazz = Exam0110.class;

    // 클래스에서 메서드 정보를 추출하기
    // => 해당 클래스에 선언된 public 메서드 + 상속 받은 public 메서드
    Method[] list = clazz.getMethods();
    for (Method m : list) {
      System.out.println(m.getName());
    }
  }

}

결과

main
m2
m1
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

 

 

메서드 정보 추출 - getDeclaredMethods()
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0120 {
  public static void m1() {}

  public void m2() {}

  protected void m3() {}

  void m4() {}

  private void m5() {}

  public static void main(String[] args) {
    Class<?> clazz = Exam0120.class;

    // => 현재 클래스에 정의된 모든 메서드
    Method[] list = clazz.getDeclaredMethods();
    for (Method m : list) {
      System.out.println(m.getName());
    }
  }

}

결과

main
m2
m1
m3
m5
m4

 

 

메서드 정보 추출 - 특정 메서드만 추출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0210 {
  public static void m1() {}

  public void m2() {}

  protected void m3() {}

  void m4() {}

  private void m5() {}

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Exam0210.class;

    // 해당 클래스에 선언된 메서드와 상속 받은 메서드까지 포함하여
    // 파라미터가 없는 "m3" 이름을 가진 public 메서드 추출
    //    Method m0 = clazz.getMethod("m3"); // public이 아니기 때문에 못 찾는다.

    Method m = clazz.getMethod("m1"); // OK!
    System.out.println(m.getName());

    System.out.println(clazz.getMethod("toString").getName());
    System.out.println("----------------------");

    // => public 이 아닌 메서드를 찾고 싶다면,
    m = clazz.getDeclaredMethod("m3"); // OK
    System.out.println(m.getName());

    // => 단 현재 클래스에 정의된 메서드를 찾는다.
    // => 상속 받은 메서드는 제외한다.
    System.out.println(clazz.getDeclaredMethod("toString")); // 예외 발생!
    // 상속 받은 메서드는 못찾는다.
  }

}

결과

m1
toString
----------------------
m3
Exception in thread "main" java.lang.NoSuchMethodException: com.eomcs.reflect.ex03.Exam0210.toString()
	at java.base/java.lang.Class.getDeclaredMethod(Class.java:2675)
	at com.eomcs.reflect.ex03.Exam0210.main(Exam0210.java:36)

 

 

메서드 정보 추출 - 특정 메서드만 추출 II
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0220 {
  public void m1() {}

  public void m2(String s) {}

  public void m3(String s, int i) {}

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Exam0220.class;

    // 파라미터가 없는 메서드를 찾을 때는 파라미터의 타입 정보를 넘기지 않는다.
    System.out.println(clazz.getMethod("m1").getName());

    // 파라미터가 있는 메서드를 찾을 때 그 파라미터의 타입 정보를 넘겨야 한다.
    // 타입정보 = 클래스 정보 = Class 객체
    Class<?> parameterType = String.class;
    Method m = clazz.getMethod("m2", parameterType);
    System.out.println(m.getName());

    // 위와 같다.
    Method m2 = clazz.getMethod("m2", String.class);
    System.out.println(m2.getName());

    m = clazz.getMethod("m2", Class.forName("java.lang.String"));
    System.out.println(m.getName());

    // primitive 타입도 클래스 정보가 있다.
    // int => int.class
    // byte,short,int,long,float,double,boolean,char 는 비록 클래스는 아니지만,
    // 일반 클래스처럼 타입 정보를 꺼낼 수 있도록 "class"라는 스태틱 변수를 제공한다.
    Class<?> intType = int.class;
    Class<?> stringType = String.class;
    m = clazz.getMethod("m3", stringType, intType);
    System.out.println(m.getName());

    m = clazz.getMethod("m3", String.class, int.class);
    System.out.println(m.getName());

    // 메서드의 파라미터 순서를 지켜야 한다.
    //    m = clazz.getMethod("m3", int.class, String.class);
    //    System.out.println(m.getName());

    System.out.println(clazz.getMethod("m3", String.class, int.class).getName());
  }

}

 

 

메서드 정보 추출 - 스태틱 메서드 호출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0310 {

  // 스태틱 메서드
  public static void plus(int a, int b) {
    System.out.printf("합계: %d\n", a + b);
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Exam0310.class;

    // 스태틱 메서드 호출 방법
    // Method.invoke(인스턴스, 아규먼트, ...);
    //

    // 1) 스태틱 메서드를 찾아 호출하기
    Method m = clazz.getMethod("plus", int.class, int.class);

    // => 스태틱 메서드이기 때문에 인스턴스는 지정할 필요가 없다.
    m.invoke(null, 10, 20);

    // => 클래스 메서드를 호출하는 일반적인 방식
    Exam0310.plus(10, 20);
  }

}

 

 

메서드 정보 추출 - 인스턴스 메서드 호출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0320 {

  // 인스턴스 메서드
  public void minus(int a, int b) {
    System.out.printf("빼기: %d\n", a - b);
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Exam0320.class;

    // 메서드 호출 방법
    // Method.invoke(인스턴스, 아규먼트, ...);
    //

    // 1) 인스턴스 메서드를 찾아 호출하기
    Method m = clazz.getMethod("minus", int.class, int.class);

    // => 인스턴스 메서드를 호출할 때는 반드시 인스턴스 주소를 넘겨야 한다.
    Exam0320 obj = new Exam0320();
    m.invoke(obj, 10, 20); // 리플렉션 API를 사용하여 인스턴스 메서드 호출

    // => 인스턴스 메서드를 호출하는 일반적인 방식
    obj.minus(10, 20);

    // => 인스턴스 주소를 넘겨주지 않으면 NullPointerException 발생!
    //    m.invoke(null, 10, 20);

  }

}

 

 

메서드 정보 추출 - 여러 개의 파라미터를 가지는 메서드 호출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0410 {

  public static void print(String name, int a, int b, int c) {
    System.out.printf("==> %s: %d\n", name, (a + b + c));
  }

  public static void main(String[] args) throws Exception {
    Method m = Exam0410.class.getMethod("print", String.class, int.class, int.class, int.class);

    System.out.println(m);

    // 파라미터 값을 낱개로 전달하기
    m.invoke(null, "홍길동", 100, 90, 80);

    // 파라미터 값을 배열에 담아서 전달할 수 있다.
    m.invoke(null, new Object[] {"홍길동", 100, 100, 100});
  }
}

 

 

메서드 정보 추출 - 배열 파라미터를 가지는 메서드 호출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0420 {

  // 배열 파라미터
  public static void print(String[] names) {
    System.out.print("==> ");
    for (String name : names) {
      System.out.print(name + ",");
    }
    System.out.println();
  }

  public static void main(String[] args) throws Exception {

    Method m = Exam0420.class.getMethod("print", String[].class);

    System.out.println(m);

    // 배열 파라미터인 경우 배열에 담아서 전달
    m.invoke(null, (Object) new String[] {"홍길동", "임꺽정", "유관순"});

    // 다음과 같이 낱개로 전달할 수 없다.
    //    m.invoke(null, "홍길동", "임꺽정", "유관순"); // 예외 발생!
  }

}

 

 

필드 정보 추출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Exam0510 {
  @SuppressWarnings("unchecked")
  public static void main(String[] args) throws Exception {
    Class<?> clazz = Car.class;

    Field[] fields = clazz.getDeclaredFields();
    for (Field f : fields) {
      System.out.printf("%s:%s\n", f.getName(), f.getType().getName());
    }


    Constructor<Car> defaultConst = (Constructor<Car>) clazz.getConstructor();
    Car car = defaultConst.newInstance();

    // 1) private 필드는 일반적인 방식으로 접근할 수 없다.
    //    car.maker = "비트자동차"; // 컴파일 오류!

    // 2) 다음과 같이 Reflection API를 사용하면 private 필드에 접근할 수 있다. 
    Field makerField = clazz.getDeclaredField("maker");

    // private modifier로 선언된 필드라 하더라도
    // 다음 메서드를 통해 접근 가능하도록 만들 수 있다.
    makerField.setAccessible(true);

    // private 필드에 값 넣기
    // 가능해?
    makerField.set(car, "비트자동차");

    System.out.println(car);
  }

}
package com.eomcs.reflect.ex03;

public class Car {
  private String maker;
  private String model;
  private int cc;

  @Override
  public String toString() {
    return "Car [maker=" + maker + ", model=" + model + ", cc=" + cc + "]";
  }
  public String getMaker() {
    return maker;
  }
  public void setMaker(String maker) {
    this.maker = maker;
  }
  public String getModel() {
    return model;
  }
  public void setModel(String model) {
    this.model = model;
  }
  public int getCc() {
    return cc;
  }
  public void setCc(int cc) {
    this.cc = cc;
  }
}

 

 

메서드 정보 추출 - 가변 파라미터를 가지는 메서드 호출
package com.eomcs.reflect.ex03;

import java.lang.reflect.Method;

public class Exam0430 {

  //가변 파라미터(varargs; variable arguments)
  public static void print(String... names) {
    System.out.print("==> ");
    for (String name : names) {
      System.out.print(name + ",");
    }
    System.out.println();
  }

  public static void main(String[] args) throws Exception {
    Method m = Exam0430.class.getMethod("print", String[].class);

    System.out.println(m);

    // 가변 파라미터인 경우에도 배열에 담아 전달
    m.invoke(null, (Object) new String[] {"홍길동", "임꺽정", "유관순"});

    // 다음과 같이 낱개로 전달할 수 없다.
    //    m.invoke(null, "홍길동", "임꺽정", "유관순"); // 예외 발생!
  }
}

 

 

com.eomcs.reflect.ex04

 

 

생성자 : 생성자 정보 가져오기
package com.eomcs.reflect.ex04;

import java.lang.reflect.Constructor;

public class Exam01 {

  public Exam01() {}

  public Exam01(int i) {}

  public Exam01(String s, int i) {}

  public static void main(String[] args) {
    Class<?> clazz = Exam01.class;

    // 생성자 목록 가져오기
    Constructor<?>[] list = clazz.getConstructors();
    for (Constructor<?> c : list) {
      System.out.printf("%s(%d)\n", c.getName(), c.getParameterCount());
    }

  }

}

 

 

생성자 : 파라미터를 가지는 생성자 알아내기
package com.eomcs.reflect.ex04;

import java.lang.reflect.Constructor;

public class Exam02 {

  public Exam02() {}

  public Exam02(int i) {}

  public Exam02(String s, int i) {}

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Exam02.class;

    // 파라미터가 없는 기본 생성자 가져오기
    Constructor<?> c1 = clazz.getConstructor();
    System.out.printf("%s(%d)\n", c1.getName(), c1.getParameterCount());

    // int 값을 받는 생성자 가져오기
    Constructor<?> c2 = clazz.getConstructor(int.class);
    System.out.printf("%s(%d)\n", c2.getName(), c2.getParameterCount());

    // String과 int 값을 순서대로 받는 생성자 가져오기
    Constructor<?> c3 = clazz.getConstructor(String.class, int.class);
    System.out.printf("%s(%d)\n", c3.getName(), c3.getParameterCount());

    // 해당 타입의 값을 받는 생성자가 없을 때?
    // => 예외 발생!
    Constructor<?> c4 = clazz.getConstructor(String.class);
    System.out.printf("%s(%d)\n", c4.getName(), c4.getParameterCount());
  }

}

 

 

생성자 : 생성자 호출하기
package com.eomcs.reflect.ex04;

import java.lang.reflect.Constructor;

public class Exam03 {
  int value;

  public Exam03(int i) {
    this.value = i;
  }

  public void print() {
    System.out.printf("value=%d\n", this.value);
  }

  public static void main(String[] args) throws Exception {
    Class<?> clazz = Exam03.class;

    // newInstance()는 객체를 생성한 후 기본 생성자를 호출한다.
    // Exam03은 기본 생성자가 없기 때문에 실행 오류가 발생한다!
    //    Exam03 obj0 = (Exam03) clazz.newInstance(); // 실행 오류!

    // 해결=> 생성자를 준비한다.
    Constructor<?> c = clazz.getConstructor(int.class);

    // 생성자 객체를 통해 인스턴스를 생성해야 한다.
    Exam03 obj = (Exam03) c.newInstance(200);
    obj.print();
  }

}

 

 

com.eomcs.reflect.ex05

 

 

파라미터 - 파라미터 정보 알아내기
package com.eomcs.reflect.ex05;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class Exam0110 {

  public void m1(String name, int age) {}

  public void m2() {}

  public void m3(File file, String name) {}

  public static void main(String[] ok) {
    Class<?> clazz = Exam0110.class;

    // 해당 클래스에 정의된 메서드를 모두 가져온다.
    Method[] methods = clazz.getDeclaredMethods();
    for (Method m : methods) {
      System.out.printf("%s:\n", m.getName());

      // 메서드의 파라미터 정보를 가져온다.
      Parameter[] parameters = m.getParameters();
      for (Parameter p : parameters) {
        System.out.printf(" %s : %s\n", p.getName(), p.getType().getName());
        // 파라미터의 이름은 argx 행태로 되어 있다.
        // .class 파일에는 분명히 파라미터 이름이 보관되어 있지만,
        // Reflection API에서는 보관된 값을 꺼낼 수 없다.
        // 꺼내려면 직접 .class 파일을 읽어서 파라미터 정보를 알아내야 한다.
        //
        // 컴파일할 때 파라미터 이름을 Reflection API에서 꺼낼 수 있도록
        // 설정해준다면, 그렇다면 원래의 파라미터 이름을 알아낼 수 있다.
        // 즉 -parameters 옵션을 추가하여 컴파일하면 Reflection API로 파라미터 이름을
        // 꺼낼 수 있다.
        // > javac -d bin/main -encoding UTF-8 -parameters src/com/eomcs/reflect/ex05/Exam01.java
        // 그런데 일반적으로 이 옵션을 붙여 컴파일 하지 않는다.
        // 그럼에도불구하고 Spring Framework나 eclipse IDE에서는
        // 메서드의 파라미터 이름을 정확하게 추출한다.
        // 그것은 Spring 프레임워크나 eclipse IDE가
        // 직접 .class 파일을 읽고 분석해서 해당 정보를 추출하기 때문이다.
        //
      }
    }
  }

}

 

 

리턴 타입
package com.eomcs.reflect.ex05;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;

public class Exam0210 {

  public String m1(String name, int age) {
    return null;
  }

  public char[] m2() {
    return null;
  }

  public ArrayList<String> m3(File file, String name) {
    return null;
  }

  public void m4() {}

  public Map<String,File> m5() {
    return null;
  }

  public static void main(String[] ok) {
    Class<?> clazz = Exam0210.class;

    // 클래스에 정의된 메서드를 모두 가져온다.
    Method[] methods = clazz.getDeclaredMethods();
    for (Method m : methods) {
      System.out.printf("%s:\n", m.getName());

      // 메서드의 리턴 타입 가져오기
      Class<?> returnType = m.getReturnType();
      System.out.printf("    리턴: %s\n", returnType.getName());
    }
  }

}

 

 

리턴 타입 - 제네릭 타입
package com.eomcs.reflect.ex05;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Map;

public class Exam0220 {

  public String m1(String name, int age) {
    return null;
  }

  public char[] m2() {
    return null;
  }

  public ArrayList<String> m3(File file, String name) {
    return null;
  }

  public void m4() {}

  public Map<String,File> m5() {
    return null;
  }

  public static void main(String[] ok) {
    Class<?> clazz = Exam0220.class;

    // 클래스에 정의된 메서드를 모두 가져온다.
    Method[] methods = clazz.getDeclaredMethods();
    for (Method m : methods) {
      System.out.printf("%s:\n", m.getName());

      // 메서드의 제네릭 리턴 타입 가져오기
      Type returnType = m.getGenericReturnType();
      System.out.printf("    리턴: %s\n", returnType.getTypeName());
      if (returnType instanceof ParameterizedType) {
        Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments();
        System.out.print("         => ");
        for (Type actualType : actualTypes) {
          System.out.print(actualType.getTypeName() + ", ");
        }
        System.out.println();
      }
    }
  }

}

 

 

메서드의 modifier
package com.eomcs.reflect.ex05;

//어떤 클래스나 인터페이스의 스태틱 멤버를 import 할 수 있다.
import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PROTECTED;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Exam0310 {

  public static void main(String[] ok) {
    Class<?> clazz = String.class;

    // 클래스에 정의된 메서드를 모두 가져온다.
    Method[] methods = clazz.getDeclaredMethods();
    for (Method m : methods) {
      System.out.printf("%s() => ", m.getName());

      int modifiers = m.getModifiers();
      if (Modifier.isPublic(modifiers))//(modifiers & Modifier.PUBLIC) == Modifier.PUBLIC)
        System.out.print(" public");
      else if ((modifiers & PROTECTED) != 0)
        System.out.print(" protected");
      else if ((modifiers & PRIVATE) != 0)
        System.out.print(" private");


      //if ((modifiers & STATIC) != 0)
      if (Modifier.isStatic(modifiers))
        System.out.print(" static");

      if ((modifiers & FINAL) != 0)
        System.out.print(" final");

      System.out.println();
    }
  }

}

 

 

메서드가 실제 정의된 클래스 알아내기
package com.eomcs.reflect.ex05;

import java.io.BufferedReader;
import java.lang.reflect.Method;

public class Exam0410 {

  public static void main(String[] ok) {
    Class<?> clazz = BufferedReader.class;

    // 수퍼 클래스의 메서드를 포함하여 모든 public 메서드를 알아낸다.
    Method[] methods = clazz.getMethods();

    for (Method m : methods) {
      // 메서드가 실제 정의된 클래스의 이름 출력하기
      System.out.printf("%s.%s()\n", 
          m.getDeclaringClass().getSimpleName(),
          m.getName());
    }
  }

}

 

 

com.eomcs.reflect.ex06

 

 

Proxy 객체를 만드는 방법
package com.eomcs.reflect.ex06.a;

import java.lang.reflect.Proxy;

// Proxy 객체를 만드는 방법
public class Exam0110 {
  public static void main(String[] args) {

    MyInterface obj = (MyInterface) Proxy.newProxyInstance(
        Exam0110.class.getClassLoader(), // 클래스를 메모리에 로딩하는 일을 할 객체
        new Class[] {MyInterface.class}, // 자동 생성할 클래스가 구현해야 하는 인터페이스 목록
        new MyInvocationHandler());

    // 자동 생성된 인터페이스 구현체의 메서드 호출하기
    obj.m1();
    obj.m2();
  }
}
package com.eomcs.reflect.ex06.a;

public interface MyInterface {
  void m1();
  void m2();
}
package com.eomcs.reflect.ex06.a;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("호출됨!");
    return null;
  }
}

 

 

호출되는 메서드를 알아내는 방법
package com.eomcs.reflect.ex06.b;

import java.lang.reflect.Proxy;

// 호출되는 메서드를 알아내는 방법
public class Exam0110 {
  public static void main(String[] args) {

    MyInterface obj = (MyInterface) Proxy.newProxyInstance(//
        Exam0110.class.getClassLoader(), // 클래스를 메모리에 로딩하는 일을 할 객체
        new Class[] {MyInterface.class}, // 자동 생성할 클래스가 구현해야 하는 인터페이스 목록
        new MyInvocationHandler());

    obj.m1();
    obj.m2();
  }
}
package com.eomcs.reflect.ex06.b;

public interface MyInterface {
  void m1();
  void m2();
}
package com.eomcs.reflect.ex06.b;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.printf("%s()가 호출됨!\n", method.getName());
    System.out.println(args);
    return null;
  }
}

 

 

값을 리턴하는 방법
package com.eomcs.reflect.ex06.c;

import java.lang.reflect.Proxy;

// 값을 리턴하는 방법
public class Exam0110 {
  public static void main(String[] args) {

    MyInterface obj = (MyInterface) Proxy.newProxyInstance(//
        Exam0110.class.getClassLoader(), // 클래스를 메모리에 로딩하는 일을 할 객체
        new Class[] {MyInterface.class}, // 자동 생성할 클래스가 구현해야 하는 인터페이스 목록
        new MyInvocationHandler());

    System.out.println(obj.m1());
    System.out.println(obj.m2());
    obj.m3();
  }
}
package com.eomcs.reflect.ex06.c;

public interface MyInterface {
  int m1();
  String m2();
  void m3();
}
package com.eomcs.reflect.ex06.c;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    switch (method.getName()) {
      case "m1":
        return 100; // 다음과 같이 auto-boxing => Integer.valueOf(100)
      case "m2":
        return "Hello!";
    }
    return null;
  }
}

 

 

값을 리턴하는 방법
package com.eomcs.reflect.ex06.d;

import java.lang.reflect.Proxy;

// 값을 리턴하는 방법
public class Exam0110 {
  public static void main(String[] args) {

    MyInterface obj = (MyInterface) Proxy.newProxyInstance(//
        Exam0110.class.getClassLoader(), // 클래스를 메모리에 로딩하는 일을 할 객체
        new Class[] {MyInterface.class}, // 자동 생성할 클래스가 구현해야 하는 인터페이스 목록
        new MyInvocationHandler());

    System.out.println(obj.m1(100, 200));
    System.out.println(obj.m2("홍길동", 20));
  }
}
package com.eomcs.reflect.ex06.d;

public interface MyInterface {
  int m1(int p1, int p2);
  String m2(String name, int age);
}
package com.eomcs.reflect.ex06.d;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    switch (method.getName()) {
      case "m1":
        int p1 = (int) args[0];
        int p2 = (int) args[1];
        return p1 + p2;
      case "m2":
        String name = (String) args[0];
        int age = (int) args[1];
        return name + "님은 나이는 " + age + "살 입니다.";
    }
    return null;
  }
}

 

 

인터페이스 구현체를 자동으로 생성하기
package com.eomcs.reflect.ex06.e;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Exam0110 {

  public static void main(String[] args) {
    // java.lang.reflect.Proxy
    // => 인터페이스 구현체를 만드는 역할을 한다.
    // 
    // newProxyInstance(
    //    만든 인터페이스 구현체를 메모리에 로딩하는 일을 도와줄 클래스로더,
    //    구현할 인터페이스 정보 목록,
    //    실제 작업을 수행하는 객체)
    // => 파라미터로 넘겨 받은 인터페이스를 모두 구현한 클래스를 만들고 인스턴스를 생성해 리턴한다.
    // 
    // 클래스 로더
    // => 클래스 정보를 로딩하는 역할을 수행한다.
    // => 클래스 로더를 얻는 방법
    //    - 클래스정보.getClassLoader()
    // => 클래스 정보
    //    - 인스턴스.getClass()
    //    - 클래스명.class
    //    - Class.forName("패키지명을 포함한 클래스명")
    //
    // 클래스 정보를 얻는 방법 예: 
    /*
    String s = "hello"; 
    Class<?> c1 = String.class; // 클래스의 static 변수인 class의 값을 사용할 수 있다.
    Class<?> c2 = s.getClass(); // 인스턴스로 알아낼 수 있다.
    Class<?> c3 = Class.forName("java.lang.String"); // 클래스 정보를 리턴하는 도구를 이용.
     */
    //
    // 실제 작업을 수행하는 객체
    // => java.lang.reflect.InvocationHandler 인터페이스에 따라 동작하는 객체
    // => 즉 InvocationHandler 구현체
    // 

    // 1) 로컬 클래스
    class MyHandler implements InvocationHandler {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke()...호출됨!");
        // newProxyInstance()가 생성한 객체에 대해 메서드를 호출할 때마다 이 메서드가 호출된다.
        int a = (int) args[0]; // auto-unboxing => ((Integer)args[0]).intValue();
        int b = (int) args[1]; // auto-unboxing => ((Integer)args[1]).intValue();

        switch (method.getName()) {
          case "plus":
            return a + b;
          case "minus":
            return a - b;
        }
        return 0;
      }
    }

    // Calculator 인터페이스를 구현한 클래스를 만들고 그 인스턴스를 생성하여 리턴한다.
    Calculator c1 = (Calculator) Proxy.newProxyInstance(
        Calculator.class.getClassLoader(), 
        new Class[] {Calculator.class}, 
        new MyHandler());

    System.out.println("++++");
    System.out.println(c1.plus(10, 20));
    System.out.println("----");
    System.out.println(c1.minus(10, 20));
  }

}
package com.eomcs.reflect.ex06.e;

public interface Calculator {
  int plus(int a, int b);
  int minus(int a, int b);
}

 

 

여러 개의 인터페이스를 구현한 객체를 자동 생성하기
package com.eomcs.reflect.ex06.e;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Exam0120 {

  public static void main(String[] args) {

    // Calculator, Calculator2, Calculator3 인터페이스를 구현한 
    // 클래스를 만들고 그 인스턴스를 생성하여 리턴한다.
    Object proxy = Proxy.newProxyInstance(
        Exam0120.class.getClassLoader(), 
        new Class[] {
            Calculator.class, 
            Calculator2.class, 
            Calculator3.class}, 
        // 2) 익명 클래스를 문법을 이용하여 InvocationHandler 구현하기
        new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // newProxyInstance()가 생성한 객체에 대해 메서드를 호출할 때마다 이 메서드가 호출된다.
            int a = (int) args[0]; // auto-unboxing => ((Integer)args[0]).intValue();
            int b = (int) args[1]; // auto-unboxing => ((Integer)args[1]).intValue();

            switch (method.getName()) {
              case "plus":
                return a + b;
              case "minus":
                return a - b;
              case "multiple":
                return a * b;
              case "divide":
                return a / b;
              case "mod":
                return a % b;
            }
            return 0;
          }
        });

    // 자동 생성된 인터페이스 구현체를 사용하기
    // 1) Object 레퍼런스로 바로 사용하기
    // => 비록 proxy 레퍼런스가 가리키는 객체가 Calculator, Calculator2, Calculator3 인터페이스를 
    //    구현한 클래스일지라도 
    //    일단 proxy 레퍼런스의 타입이 Object 이기 때문에 
    //    바로 인터페이스의 메서드를 호출할 수 없다.
    // => 해결책?
    //    레퍼런스를 바로 사용하지 말고 해당 인터페이스로 형변환 한 다음에 사용하라!
    // 
    //    int result = 0;
    //    result = proxy.plus(10, 20); // 컴파일 오류! 
    //    result = proxy.minus(10, 20); // 컴파일 오류! 
    //    result = proxy.multiple(10, 20); // 컴파일 오류! 
    //    result = proxy.divide(10, 20); // 컴파일 오류! 
    //    result = proxy.mod(10, 20); // 컴파일 오류! 

    Calculator c1 = (Calculator) proxy;
    System.out.println(c1.plus(10, 20));
    System.out.println(c1.minus(10, 20));

    Calculator2 c2 = (Calculator2) proxy;
    System.out.println(c2.multiple(10, 20));
    System.out.println(c2.divide(10, 20));

    Calculator3 c3 = (Calculator3) proxy;
    System.out.println(c3.mod(10, 20));
  }

}
package com.eomcs.reflect.ex06.e;

public interface Calculator2 {
  int multiple(int a, int b);
  int divide(int a, int b);
}
package com.eomcs.reflect.ex06.e;

public interface Calculator3 {
  int mod(int a, int b);
}

 

 

여러 개의 인터페이스를 구현한 객체를 자동 생성하기 - 람다(lambda) 적용=
package com.eomcs.reflect.ex06.e;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Exam0130 {

  public static void main(String[] args) {

    // Calculator, Calculator2, Calculator3 인터페이스를 구현한 
    // 클래스를 만들고 그 인스턴스를 생성하여 리턴한다.
    Object proxy = Proxy.newProxyInstance(
        Exam0130.class.getClassLoader(), 
        new Class[] {
            Calculator.class, 
            Calculator2.class, 
            Calculator3.class}, 
        // 3) 람다(lambda) 문법을 사용하여 InvocationHandler 구현하기
        (Object proxyObj, Method method, Object[] params) -> {
          // newProxyInstance()가 생성한 객체에 대해 메서드를 호출할 때마다 이 메서드가 호출된다.
          int a = (int) params[0]; // auto-unboxing => ((Integer)args[0]).intValue();
          int b = (int) params[1]; // auto-unboxing => ((Integer)args[1]).intValue();

          switch (method.getName()) {
            case "plus":
              return a + b;
            case "minus":
              return a - b;
            case "multiple":
              return a * b;
            case "divide":
              return a / b;
            case "mod":
              return a % b;
          }
          return 0;
        });

    Calculator c1 = (Calculator) proxy;
    Calculator2 c2 = (Calculator2) proxy;
    Calculator3 c3 = (Calculator3) proxy;

    System.out.println(c1.plus(10, 20));
    System.out.println(c1.minus(10, 20));
    System.out.println(c2.multiple(10, 20));
    System.out.println(c2.divide(10, 20));
    System.out.println(c3.mod(10, 20));
  }

}

 

 

Proxy 를 이용한 DAO 구현체 자동 생성하기
package com.eomcs.reflect.ex06.f;

import java.util.HashMap;
import java.util.List;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.ioc.ex01.ApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/reflect/ex06/f/application-context.xml");

    SpringUtils.printBeanList(iocContainer);

    BoardDao boardDao = iocContainer.getBean(BoardDao.class);

    //1) 게시물 입력
    Board board = new Board();
    board.setTitle("제목입니다.");
    board.setContent("내용입니다.");
    boardDao.insert(board);

    //2) 게시물 목록 조회
    // => selectList()의 파라미터 값을 한 개만 넘겨야 하기 때문에
    // 여러 개의 값을 넣고 싶으면 Map에 담아 넘긴다.
    HashMap<String,Object> params = new HashMap<>();
    params.put("startIndex", 0);
    params.put("pageSize", 5);

    List<Board> list = boardDao.selectList(params);
    for (Board b : list) {
      System.out.printf("%d, %s, %s\n",
          b.getNo(),
          b.getTitle(),
          b.getRegisteredDate());
    }
  }

}

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.eomcs.reflect.ex06.f"/>

    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="mapperLocations" 
                value="classpath*:com/eomcs/reflect/ex06/f/*Mapper.xml" />  
      <property name="typeAliases" 
                value="com.eomcs.reflect.ex06.f.Board"/>
    </bean>
    
    <bean id="dataSource" 
          class="org.apache.commons.dbcp.BasicDataSource" 
           destroy-method="close">
       <property name="driverClassName" value="${jdbc.driver}" />
       <property name="url" value="${jdbc.url}" />
       <property name="username" value="${jdbc.username}" />
       <property name="password" value="${jdbc.password}" />
    </bean>

    <context:property-placeholder 
        location="com/eomcs/reflect/ex06/f/jdbc.properties"/>	
        
    <bean id="boardDao" class="com.eomcs.reflect.ex06.f.BoardDaoGenerator"/>
</beans>

 

# key=value
jdbc.driver=org.mariadb.jdbc.Driver
jdbc.url=jdbc:mariadb://localhost:3306/studydb
jdbc.username=study
jdbc.password=1111

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="com.eomcs.reflect.ex06.f.BoardDao">

  <resultMap type="Board" id="BoardMap">
    <id column="board_id" property="no"/>
    <result column="title" property="title"/>
    <result column="contents" property="content"/>
    <result column="created_date" property="registeredDate"/>
    <result column="view_count" property="viewCount"/>
  </resultMap>
  
  <select id="selectList" resultMap="BoardMap" parameterType="map">
    select 
      board_id, 
      title, 
      contents, 
      created_date 
    from x_board
    order by board_id desc
    limit #{startIndex}, #{pageSize}
  </select>
  
  <insert id="insert" parameterType="Board">
    insert into x_board(title,contents) 
    values(#{title},#{content})
  </insert>
  
  <delete id="delete" parameterType="int">
    delete from x_board 
    where board_id=#{no}
  </delete>
</mapper>

 

package com.eomcs.reflect.ex06.f;

import java.io.Serializable;
import java.sql.Date;

public class Board implements Serializable {
  private static final long serialVersionUID = 1L;

  int no;
  String title;
  String content;
  Date registeredDate;
  int viewCount;

  @Override
  public String toString() {
    return "Board [no=" + no + ", title=" + title + ", content=" + content + ", registeredDate="
        + registeredDate + ", viewCount=" + viewCount + "]";
  }

  public int getViewCount() {
    return viewCount;
  }

  public void setViewCount(int viewCount) {
    this.viewCount = viewCount;
  }

  public int getNo() {
    return no;
  }

  public void setNo(int no) {
    this.no = no;
  }

  // 프로퍼티명 : title
  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  // 프로퍼티명 : content
  public String getContent() {
    return content;
  }

  public void setContent(String content) {
    this.content = content;
  }

  // 프로퍼티명 : registeredDate
  public Date getRegisteredDate() {
    return registeredDate;
  }

  public void setRegisteredDate(Date registeredDate) {
    this.registeredDate = registeredDate;
  }
}

 

package com.eomcs.reflect.ex06.f;

import java.util.List;
import java.util.Map;

public interface BoardDao {
  List<Board> selectList(Map<String, Object> map);

  int insert(Board board);

  int delete(int no);
}

 

package com.eomcs.reflect.ex06.f;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;

public class BoardDaoGenerator implements FactoryBean<BoardDao>{
  @Autowired SqlSessionFactory sqlSessionFactory;

  @Override
  public BoardDao getObject() throws Exception {
    return (BoardDao) Proxy.newProxyInstance(
        this.getClass().getClassLoader(),
        new Class[] {BoardDao.class},
        new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Class<?> interfaceType = proxy.getClass().getInterfaces()[0];
            String namespace = interfaceType.getName();
            String sqlId = method.getName();
            String statementName = namespace + "." + sqlId;

            Class<?> returnType = method.getReturnType();

            SqlSession sqlSession = sqlSessionFactory.openSession();

            if (returnType == void.class || returnType == int.class) {
              if (args.length == 0) {
                return sqlSession.insert(statementName);
              } else {
                return sqlSession.insert(statementName, args[0]);
              }
            } else if (returnType == List.class) {
              if (args.length == 0) {
                return sqlSession.selectList(statementName);
              } else {
                return sqlSession.selectList(statementName, args[0]);
              }
            } else {
              if (args.length == 0) {
                return sqlSession.selectOne(statementName);
              } else {
                return sqlSession.selectOne(statementName, args[0]);
              }
            }
          }
        });
  }

  @Override
  public Class<?> getObjectType() {
    return BoardDao.class;
  }
}