이러던 와중에 자바의 정석 저자이신 남궁성 선생님의 "Spring의 정석" 을 수강하다가 재밌는 것을 발견했다.
public class PrivateClass { private void method1() { System.out.println("private Method1"); } private void method2(int a,int b) { int c = a+b; System.out.println("a + b = " +c ); } }
호출의 대상이 될 PrivateClass를 작성해 주었다.
Method1은 매개변수가 없는 메서드로 작성하였고, Method2는 매개변수를 입력받아, 연산을 하고 출력해주는 형식으로 작성하였다.
import java.lang.reflect.Method; public class MethodCall2 { public static void main(String[] args) throws Exception{ PrivateClass pv = new PrivateClass(); Class clazz = pv.getClass(); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("method1"); method.setAccessible(true); method.invoke(obj); } }
Private Method를 호출하기 위해서는 Spring의 정석에서 남궁성 선생님은 Class.forname()을 통해서 정보를 가져오셨는데, 약간 다른 방법을 생각해보다가 PrivateClass를 객체생성해서 Class타입의 clazz 변수에 getClass() 메서드를 이용해서 클래스 객체를 리턴받게 하였다.
예외가 발생할 수 있으므로 throws Exception을 붙여주었고,
newInstance() 메서드는 반환타입이 Object이므로 Object타입의 참조변수 obj에 값을 저장해준다.
Method 타입의 참조변수 method에 Class의 getDeclareMethod() 메서드를 이용해서 "메서드이름"을 인자값으로 주면 참조변수 method에 메서드의 정보가 저장된다.
Method 클래스의 setAccessible() 메서드에 인자값으로 true를 주면 해당 메서드를 사용할 수 있도록 상태를 변경한것이다.
Method 클래스의 invoke() 메서드의 인자값으로 클래스의 정보를 가지고 있는 참조변수 obj의 값을 넣어주고 실행하면,
output:
금기의 영역인줄 알았던 Private Method가 실행된다!
너무 신기해서 자주자주 활용해보려 했지만, 저렇게 Private Method를 끌어다가 쓰는 건 별로 좋지 않은 것이라고 한다.
import java.lang.reflect.Method; public class MethodCall3 { public static void main(String[] args) throws Exception{ Class clazz = Class.forName("TJ5.PrivateClass"); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("method2", int.class,int.class); method.setAccessible(true); method.invoke(obj, 4,2); } }
아까는 객체를 생성해서 getClass()로 정보를 얻어왔지만,
Class.forName()으로도 가능하다.. 하지만 forName() 메서드를 사용할 경우에는 패키지명.Class 형식을 지켜줘야 한다.
Object 타입의 obj 변수에 newInstance() 메서드로 클래스의 객체를 반환 받는 것 까지는 이전과 같다.
예외가 발생할 수 있으므로 throws Exception을 붙여주었고,
Method타입의 method 변수에 getDeclaredMethod() 메서드를 이용하여 메서드의 정보를 가져왔다.
매개변수(Parameter)가 존재하는 메서드의 경우에는 getDeclaredMethod()의 인자 값으로,
이런식으로 넣어주면 되는데, 매개변수의 타입은 문자열이면
String.class, 정수형이라면 int.class를 넣어주면 되고 나머지 기본자료형도 자료형.class 이런식으로 순서대로 나열해 주면 된다.
위에서와 같이 setAccessible() 메서드에 true 인자값을 전달하여 메서드를 사용가능하게 해주고,
invoke() 메서드의 인자값으로 obj(클래스의 정보를 담고 있는 참조변수), 계산할 숫자1(정수형), 계산할 숫자1(정수형) 을 넣어주고 실행하면,
이렇게 4와 2 가 a,b 값에 대입되서 계산이 이루어진뒤 출력 되는 것을 볼 수 있다.
앞으로 더 많은 것들을 접하고 경험하는 시기가 왔으면 좋겠다 오늘도 화이팅 !