18
trySetAccessible?
보통 자바에서 신규 프로젝트를 시작하면, 보통 8
, 11
버전에서 시작할 것이다. 왜냐고? 지금 기준으로 보안 업데이트를 제공해주는 LTS 버전이니까.
아 물론 자바 8은 일반 제공이 이미 종료됐으며, 보안 종료도 얼마 안 남았다.
그리고 올 가을이 되면 새로운 LTS 버전인 17
으로 시작할 수도 있겠지.
단, 오라클은 웃기게도 LTS조차 숨기고 최신 버전만 제공하고 있다. 물론 어자피 써봐야 오라클과 법정 싸움 거는 거나 마찬가지니.
만약 11로 시작하고, 가끔 리플렉션으로 속성 깐다거나 할 경우,
setAccessible(boolean)
메소드와 함께 trySetAccessible()
이라는 생소한 메소드를 목격할 것이다.
이 메소드는 java 9부터 생겨난 메소드인데,
지금부터 무서운 얘기를 하도록 하겠다.
단도직입적으로 말하겠다.
Java 9 이상으로 컴파일한 클래스는 자바 8 이전과는 달리, 기본적으로, 절대로! 리플렉션을 통한 private
멤버 접근이 불가능하다.
2021-08-30: Java 9 이상부터 항상 접근 불가능한 건 아니고, 내부 및 외부 클래스에 Module-info.java
파일을 통해 모듈 관리에 속하지 않거나, 같은 모듈 내 클래스는 여전히 접근 가능하다.
내가 말한 경우 외라면, setAccessible(true)
걸어봐야, 너에게 다가오는 건 InaccessibleObjectException
예외가 반겨줄 것이다.
이게 가능하려면 두 가지 경우 뿐이다.
- 특정 클래스가 자바 8 이하에서 컴파일했을 경우
- 까려는 클래스가 네
클래스모듈 안에 있을 경우
만약 굳이 자바 8 이하에서 setAccesible
접근을 막고 싶으면 SecurityManager
클래스를 네 클래스로더에 주입해야 한다. 그렇지 않으면 기본적으로 다른 앱이나 라이브러리가 private
접근이 가능하다. 이를 관장하는 메소드는 checkPermission
이다.
하지만 공유 라이브러리를 만들려고 할 경우 이 방법은 추천하지 않는다. 빈대 잡으려다 초가삼간 태울 수 있다. 게다가 자바 17부터 제거 수순 밟고 있다! (2021-08-30) 즉, 접근 못 하게 하고 싶으면 자바 9 이상의 모듈화 말고는 방법 없다.
이런 불행 중에 다행도 있다.
대부분의 라이브러리는 자바 8 환경에서 컴파일한다.
그리고, 너희들이 많이 쓰는 Spring Framework의 경우, 자바 8 환경에서 컴파일하되, 자바 버전에 따라 지원하지 않는 API를 발견 시, 대체 API를 사용하거나, 심할 경우 아예 없애버릴 정도로 왠만한 자바 버전에서 최적의 상태로 돌아가도록 신경을 많이 써주고 있다.
내가 Jooby를 도입하기 위해 여러 쓸데없는 삽질 끝에 구축을 성공했는데, Jooby에서 부족한 부분이 있을 경우 채우는데, 치사하게 중요한 필드를 prviate
로 묶어버려서 빈을 못 불러와 trySetAccessible
메소드로 걷어서 해결했다.
물론 Jooby는 API 레이어, 스프링은 비즈니스 레이어다.
10여년 전 웹 레이어를 Struts, 비즈니스 레이어를 Spring 으로 했던 프로젝트와 데자뷰인 것 같다.
끗.
아, 하나 깜박했네, trySetAccessible()
사용법은 간단하다.
어자피 기존 setAccessible(boolean)
사용 사례로 봐야 인자에 true
넣는 경우 단 하나뿐이기 때문에, 자바 9 이상에서 trySetAccessible
메소드를 대신하여 엘레강~스한 코드와 함께 InaccessibleObjectException
예외를 회피할 수 있다.
Class<?> cls = IWannaReflectThis.class;
Field field = cls.getDeclaredField("iWannaAccessThisField");
if(field.trySetAccessible()) {
System.out.println("필드 까본 결과: " + field.get(gotcha));
} else {
System.out.println("에엑따! 자바9이상이다!");
}
2021-08-09 내용 추가
18