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

Java Reflection Exception : IllegalAccessException,IllegalArgumentException, InvocationTargetException 설명

by 왕 달팽이 2019. 6. 1.
반응형

자바 리플렉션을 이용해서 클래스의 메소드를 호출할 일이 있었다. 클래스에 있는 모든 Getter 메소드를 호출해서 멤버 값을 가져오는 코드를 다음과 같이 작성했다.

import java.lang.reflect.Method;

public class Test {

    private String name;

    private String address;

    public Test(String name, String address) {

        this.name = name;
        this.address = address;
    }

    public String getName() {

        return name;
    }

    public String getAddress() {

    return address;
    }

    private static boolean isGetter(Method method) {

        if (!method.getName().startsWith("get")) return false;

        if (method.getParameterTypes().length != 0) return false;

        return !(void.class.equals(method.getReturnType()));
    }

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

        Test test = new Test("Dave", "Seoul");

        Method[] methods = Test.class.getMethods();

        for (Method method : methods) {

            if (isGetter(method)) {
                Object result = method.invoke(test);

                System.out.println(method.getName() + ":" + result);
            }
        }
    }
}

이 코드를 실행하면 다음과 같은 결과를 얻게 된다.

getAddress:Seoul
getName:Dave
getClass:class Test

리플렉션으로 메소드를 호출하는 코드의 핵심은 'method.invoke(test)' 부분이다. 이 코드는 'test.method()' 호출과 동일한 효과를 나타낸다.

 

중요한건 Method 클래스의 invoke() 메소드가 IllegalAccessException, IllegalArgumentException, InvocationTargetException 이렇게 3가지 Exception을 던진다는 것이다. 이번 포스트에서는 이 3가지 예외가 어떨때 발생하는지 정리를 해보겠다.

IllegalAccessException

IllegalAccessException은리플렉션으로 접근하려는 클래스에 접근 제어로 접근할 수 없을 경우걸려있을 때 발생한다.

 

다음 코드를 보자.

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

public class Test {

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

        Set set = new HashSet();
		set.add("A");
		set.add("B");
		set.add("C");

		Method method = set.iterator().getClass().getMethod("hasNext");
		Object obj = method.invoke(set.iterator());
		System.out.println(obj);
	} 
}

얼핏보면 문제가 없어보인다. Iterator의 클래스를 얻어 온 다음 hasNext() 메소드를 가져와서 실행하는 코드다.

 

이 코드를 실행하면,

Exception in thread "main" java.lang.IllegalAccessException: Class Test can not access a member of class java.util.HashMap$HashIterator with modifiers "public final"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:110)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:262)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:254)
at java.lang.reflect.Method.invoke(Method.java:599)
at Test.main(Test.java:15)

이런 Exception을 얻게 된다.

 

위 코드를 다음과 같이 고치면 정상적으로 수행된다.

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

public class Test {

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

		Set set = new HashSet();
		set.add("A");
		set.add("B");
		set.add("C");

		Method method = Iterator.class.getMethod("hasNext");
		Object obj = method.invoke(set.iterator());
		System.out.println(obj);
	} 
}

설명을 하자면, set.iterator.getClass()의 결과는 "class java.util.HashMap$KeyIterator"이고, Iterator.class의 결과는 "interface java.util.Iterator"다. 전자의 경우 KeyIterator 클래스의 접근 제어자가 'private'이기 때문에 invoke() 메소드가 수행되는 코드 부분에서 접근할 수 없다. 

 

따라서 IllegalAcceessException이 발생한 것이다. invoke() 호출시 이런 예외를 잘 처리해줘야한다.

InvocationTargetException

InvocationTargetException은 리플렉션으로 호출한 메소드에서 발생한 예외를 Wrapping 한 예외 클래스다.

 

다음 코드를 실행시켜 보면,

import java.lang.reflect.Method;

public class Test {

	private String name;

	private String address;

	public Test(String name, String address) {

		this.name = name;
		this.address = address;
	}

	public String getName() {

		throw new RuntimeException();
	}

	public String getAddress() {

		return address;
	}

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

		Test test = new Test("Dave", "Seoul");

		Method method = Test.class.getMethod("getName");
		method.invoke(test);
	}
}

리플렉션으로 getName 메소드를 호출하는 과정에서 RunTimeException이 발생하게 되고, 리플렉션에서 이를 InvocationTargetException으로 Wrapping 해서 던져준다.

Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at Test.main(Test.java:30)
Caused by: java.lang.RuntimeException
at Test.getName(Test.java:17)
... 5 more

InvocationTargetException 객체의 .getTargetException() 메소드를 호출하면 메소드에서 던져진 원래 Exception을 얻을 수 있다. InvocationTargetException으로 Wrapping한 이유는 리플렉션 호출 자체의 실패와 호출된 메소드의 Exception을 구분하기 위해서라고 보면 된다.

IllegalArgumentException

IllegalArgumentException은 invoke() 로 실행할 메소드에 필요한 인자(Argument)가 제대로 설정되지 않았을 경우 발생한다.

다음 코드를 살펴보자

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {

	private String name;

	private String address;

	public Test(String name, String address) {

		this.name = name;
		this.address = address;
	}

	public String getName(String arg) {

		return this.name + arg;
	}

	public String getAddress() {

		return address;
	}

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

		Test test = new Test("Dave", "Seoul");

		Method method = Test.class.getMethod("getName", String.class);

		method.invoke(test);
    }
}

 

getName() 이라는 메소드를 리플렉션으로 가져왔다. 이 때, getName() 메소드는 String 타입의 Argument 하나를 받는다.

이후 invoke() 메소드를 이용해서 메소드를 실행시키는데, Argument를 주지 않고 실행했다. 이 경우 필요한 Argument를 명시하지 않았으므로 IllegalArgumentException이 발생하게 된다. (필요한 인자가 전달되지 않았으므로 실행할 수 없다.)

 

 

리플렉션을 사용할 때 발생할 수 있는 3가지 Exception을 알아봤다. 각 Exception 들의 의미를 잘 이해하고 사용하자.

반응형

댓글