본문 바로가기
카테고리 없음

Java Reflection API

by 용용이아바이 2024. 3. 7.
728x90

Java Reflection API를 사용하여 런타임에 메소드를 호출하는 방법을 간략하게 살펴보겠다.

예제 클래스 생성

public class Operations {
    public double publicSum(int a, double b) {
        return a + b;
    }

    public static double publicStaticMultiply(float a, long b) {
        return a * b;
    }

    private boolean privateAnd(boolean a, boolean b) {
        return a && b;
    }

    protected int protectedMax(int a, int b) {
        return a > b ? a : b;
    }
}

메소드 객체 획득

먼저 호출하려는 메서드를 반영하는 Method 객체를 가져와야 한다 . 메소드 가 정의된 유형을 나타내는 Class 객체는 이를 수행하는 두 가지 방법을 제공한다.

getMethod()

getMethod()를 사용하여 클래스나 해당 슈퍼클래스의 공개 메서드를 찾을 수 있다. 기본적으로 메소드 이름을 첫 번째 인수로 받고 그 뒤에 메소드 인수 유형을 받는다.

Method sumInstanceMethod
  = Operations.class.getMethod("publicSum", int.class, double.class);

Method multiplyStaticMethod
  = Operations.class.getMethod(
    "publicStaticMultiply", float.class, long.class);

getDeclaredMethod()

getDeclaredMethod()를 사용하여 모든 종류의 메소드를 얻을 수 있다. 여기에는 공개, 보호, 기본 액세스 및 비공개 메소드가 포함되지만 상속된 메소드는 제외된다. getMethod() 와 동일한 매개변수를 받는다.

Method andPrivateMethod
  = Operations.class.getDeclaredMethod(
    "privateAnd", boolean.class, boolean.class);
Method maxProtectedMethod
  = Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);

Instance Methods

 

인스턴스 메서드를 호출하려면 호출() 에 대한 첫 번째 인수는 호출되는 메서드를 반영하는 Method 의 인스턴스여야 한다.

@Test
public void givenObject_whenInvokePublicMethod_thenCorrect() {
    Method sumInstanceMethod
      = Operations.class.getMethod("publicSum", int.class, double.class);

    Operations operationsInstance = new Operations();
    Double result
      = (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);

    assertThat(result, equalTo(4.0));
}

Static Methods

 

이러한 메서드는 인스턴스 호출을 요구하지 않으므로 null을 첫 번째 인수로 전달할 수 있다.

@Test
public void givenObject_whenInvokeStaticMethod_thenCorrect() {
    Method multiplyStaticMethod
      = Operations.class.getDeclaredMethod(
        "publicStaticMultiply", float.class, long.class);

    Double result
      = (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);

    assertThat(result, equalTo(7.0));
}

Method Accessibility

 

기본적으로 반영된 모든 메서드에 액세스할 수 있는 것은 아니다이는 JVM이 호출 시 액세스 제어 검사를 시행함을 의미한다. 예를 들어 정의 클래스 외부에서 비공개 메서드를 호출하거나 하위 클래스나 클래스 패키지 외부에서 보호 메서드를 호출하려고 하면 IllegalAccessException이 발생 한다.

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokePrivateMethod_thenFail() {
    Method andPrivateMethod
      = Operations.class.getDeclaredMethod(
        "privateAnd", boolean.class, boolean.class);

    Operations operationsInstance = new Operations();
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokeProtectedMethod_thenFail() {
    Method maxProtectedMethod
      = Operations.class.getDeclaredMethod(
        "protectedMax", int.class, int.class);

    Operations operationsInstance = new Operations();
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);
    
    assertThat(result, equalTo(4));
}

AccessibleObject # setAccessible

반영된 메소드 객체에 대해 setAccesible(true)를 호출함으로써 JVM은 액세스 제어 검사를 억제 하고 예외를 발생시키지 않고 메소드를 호출할 수 있도록 한다.

@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() throws Exception {
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    andPrivatedMethod.setAccessible(true);

    Operations operationsInstance = new Operations();
    Boolean result = (Boolean) andPrivatedMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

AccessibleObject#canAccess

Java 9에는 호출자가 반영된 메소드 객체에 액세스할 수 있는지 확인하는 새로운 방법이 제공된다. 이를 위해 더 이상 사용되지 않는 isAccessible 메소드를 대체하여 canAccess를 제공한다.

@Test
public void givenObject_whenInvokePrivateMethod_thenCheckAccess() throws Exception {
    Operations operationsInstance = new Operations();
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
 
    assertFalse(isAccessEnabled);
 }

setAccessible(true) 를 사용하여 액세스 가능 플래그를 true 로 설정하기 전에 canAccess를 사용하여 호출자가 이미 반영된 메서드에 액세스할 수 있는지 확인할 수 있다 .

AccessibleObject#trySetAccessible

trySetAccessible은 리플렉션된 객체에 접근 가능하도록 만드는 데 사용할 수 있는 또 다른 편리한 메서드다. 이 새로운 메소드의 좋은 점은 액세스를 활성화할 수 없는 경우 false를반환한다는 것이다. 그러나 이전 메서드 setAccessible(true)는 실패할 경우 InaccessibleObjectException을  발생시킨다. trySetAccessible 메소드 의 사용을 예시해 보겠습니다 .

@Test
public void givenObject_whenInvokePublicMethod_thenEnableAccess() throws Exception {
    Operations operationsInstance = new Operations();
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    andPrivatedMethod.trySetAccessible();
    boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
        
    assertTrue(isAccessEnabled);
}

리플렉션을 통해 런타임에 클래스의 인스턴스 및 정적 메서드를 호출하는 방법을 살펴보았다. 또한 비공개 및 보호 메서드를 호출할 때 Java 액세스 제어 검사를 억제하기 위해 반영된 메서드 개체의 액세스 가능 플래그를 변경하는 방법도 보여주고 있다. 예제 코드는 Github에서 찾아볼 수 있다 .

728x90