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

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 들의 의미를 잘 이해하고 사용하자.

반응형

댓글