WARNING: An illegal reflective access operation has occurred 이 떴다. - 우아한 테크 코스 5기 숫자 야구
우테코 숫자야구를 하던 중, 거의 다 완성한 후 실행을 해 보았더니
이런 경고가 떴고, 이후에 숫자를 입력하면 별 문제없이 진행되는 문제가 발생하였다.
우선 이 문제가 뜨는 이유는 'reflective access operation has occurred' 라는데, 이 reflective access 라는 것이 자바 8 까지는 문제가 없었는데 11버전은 캡슐화가 강화되어 금지시키진 않고 warning으로 "지금 reflective access operation 을 하고있어!" 라고 알려주는 것이다. 이걸 없애는 방법을 옆의 형이 알려주었다.
인텔리제이 오른쪽 상단의 저 Application을 눌러보자.
Edit configuration 에서
저 --illegal-access=debug 를
--illegal-access=deny
--illegal-access=warn
--illegal-access=permit
으로 바꾸고 각각 실행을 해보자.
저 뒤에 붙는 옵션들은 생성되는 워닝들을 어느 정도까지 보여줄지 정하는 옵션이라 생각하면 된다.
쉽게 말해 debug는 어디서부터 시작해 코드 몇번째 줄에서 워닝이 나오는지 알려주는 것이고,
deny는 대부분의 워닝들을 무시하겠다는 뜻이다.
deny로 설정했을 때 빨간 줄 들이 많이 없어진 것을 확인 할 수 있다.
저 "unable to determine if the scanner is closed" 는 어디서 튀어나오는 애일까?
우테코에서 준 Console.java 에서 답을 찾을 수 있다.
private static boolean isClosed() {
try {
final Field sourceClosedField = Scanner.class.getDeclaredField("sourceClosed");
sourceClosedField.setAccessible(true);
return sourceClosedField.getBoolean(scanner);
} catch (final Exception e) {
System.out.println("unable to determine if the scanner is closed.");
}
return true;
}
위의 어딘가에서 reflective access 가 발생했다는 뜻이고, 사실 그래서 결론은 저 isClosed()가 있는 Console이 Readonly 이기 때문에 워닝을 없애는 일은 불가능하다는 것이다. 이제 나름대로 어디서, 왜 발생했는지 써볼 생각인데 틀렸을 가능성이 높고 어차피 해결될 문제가 아니라서 도움이 안될 것 같당!
이전까지는 우테코 프리코스 숫자야구가 자바 버전8 에서 돌아갔는데, 이번에 11로 변경되면서 reflective access 에 대한 warning이 강화되어서 경고가 뜨는 것 일 뿐 우리 코드에 문제는 없다고 믿자!
scanner = new Scanner(System.in);
int i = scanner.nextInt();
System.out.println("i = " + i);
try {
final Field sourceClosed = scanner.getClass().getDeclaredField("sourceClosed");
sourceClosed.setAccessible(true);
System.out.println(sourceClosed.getBoolean(scanner));
System.out.println("dddddddddd");
System.out.println(sourceClosed.getName());
System.out.println(sourceClosed.getType());
System.out.println(scanner);
} catch (Exception e) {
System.out.println("exception = " + e);
}
다음과 같이 isClosed 메소드를 이해하기 위한 코드를 써보았다. try에서 Exception이 나오면 바로 잡아낸다.
우선 Field 객체 sourceClosed를 생성해 Scanner 클래스의 객체 scanner의 sourceClosed 라는 필드 변수(private 으로 선언된!!)를저장한다. 여기서 Reflective Access 가 꽤 신기한 일을 한단 걸 알 수 있다. 분명 다른 클래스의 private 변수인데, getClass() 를 이용해 이름으로 검색해 찾아와 Field 객체에 저장해준다. 허허허;
암튼 가져와서, setAccessible을 통해 접근을 false 에서 true로 바꾸어 준다. 이는 Scanner 에 대해 좀 알아야 하는 내용이었는데, scanner의 sourceClosed 라는 boolean 은 열려있을 때 false, 닫히면 true 값을 갖는다.
원래 scanner를 쓰고 난 후에 scanner.closed() 이걸 써서 닫아주어야 하는데, gc가 알아서 해줘서 안하고 있던 것이지.
이 sourceClosed란 boolean 값이 private인데 getClass로 가져와, 그 값을 setAccessible로 true 로 변경해주는 것이다. 다른 클래스의 필드 변수값을 메서드 영역 (JVM에서 처음에 메모리가 생성될 때 클래스들이 저장되는 영역) 에서 가져와 변경한다. 이런 행위가 자바 버전 8에선 문제삼지 않았는데 11에선 문제가 된다는 것이다!! 잘 이해하지 못해서 설명도 구질거리는데 도움이 조금이나마 되었으면 좋겠다.