[java] 불법 반사 접근이란 무엇입니까

Java 9의 불법 반사 액세스에 대한 많은 질문이 있습니다.

이제 Google이 내뿜는 모든 것이 오류 메시지를 해결하려는 사람들이기 때문에 찾을 수없는 것은 실제로 불법 반사 액세스가 무엇인지입니다.

그래서 제 질문은 매우 간단합니다.

불법 반사 액세스를 정의하는 것은 무엇이며 어떤 상황에서 경고가 발생합니까?

Java 9에 도입 된 캡슐화 원칙과 관련이 있지만 모든 것이 어떻게 묶이고 설명을 찾을 수없는 시나리오에서 경고를 유발하는 원인이 무엇인지 모았습니다.



답변

모듈 및 해당 패키지 간의 액세스에 대한 이해는 별도로. 나는 그것의 핵심이 Module System # Relaxed-strong-encapsulation에 있다고 믿고 , 질문에 답하기 위해 그것의 관련 부분을 뽑을 것입니다.

불법 반사 액세스를 정의하는 것은 무엇이며 어떤 상황에서 경고가 발생합니까?

Java-9 로의 마이그레이션을 돕기 위해 모듈의 강력한 캡슐화를 완화 할 수 있습니다.

  • 구현은 정적 액세스 , 즉 컴파일 된 바이트 코드를 제공 할 수 있습니다 .

  • 이름이 지정되지 않은 모든 모듈의 코드, 즉 클래스 경로의 코드에 대해 열려있는 하나 이상의 모듈 패키지로 런타임 시스템을 호출하는 수단을 제공 할 수 있습니다. 런타임 시스템이 이런 방식으로 호출되고 그렇게함으로써 리플렉션 API의 일부 호출이 성공하면 실패했을 것입니다.

그러한 경우에, 당신은 실제로 “불법적 인” 반사적 접근을하게 된 것입니다 . 순수한 모듈 식 세계에서는 그러한 접근을 할 의도가 없었기 때문입니다.

이 모든 것이 어떻게 결합되고 어떤 시나리오에서 경고를 유발하는 요인은 무엇입니까?

이러한 캡슐화 완화는 런타임시 --illegal-accessJava9에서 기본적으로 permit. permit모드 보장하지만

이러한 패키지에 대한 첫 번째 반사 액세스 작업으로 인해 경고가 발생하지만 그 이후에는 경고가 발생하지 않습니다. 이 단일 경고는 추가 경고를 활성화하는 방법을 설명합니다. 이 경고는 억제 할 수 없습니다.

모드는 값 debug(이러한 모든 액세스에 대한 스택 추적 및 warn메시지 ), (이러한 각 액세스에 대한 메시지) 및 deny(해당 작업 비활성화) 값으로 구성 할 수 있습니다.


응용 프로그램에서 디버깅하고 수정해야 할 사항은 다음과 같습니다.

  • 이러한 지시문 ( )을 포함하는 모듈 선언 이나 VM 인수의 명시 적 사용 없이 한 모듈에서 다른 모듈로 패키지를 여는--illegal-access=deny 것을 알고 방지하려면 함께 실행하십시오 .opens--add-opens
  • 컴파일 된 코드에서 JDK 내부 API 로의 정적 참조 jdeps--jdk-internals옵션 과 함께 도구를 사용하여 식별 할 수 있습니다.

잘못된 반사 액세스 조작이 발견 될 때 발행되는 경고 메시지의 형식은 다음과 같습니다.

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

어디:

$PERPETRATOR 문제의 반영 작업을 호출 한 코드와 코드 소스 (예 : JAR 파일 경로)를 포함하는 형식의 정규화 된 이름입니다 (사용 가능한 경우).

$VICTIM 둘러싸는 유형의 완전한 이름을 포함하여 액세스중인 멤버를 설명하는 문자열입니다.

이러한 샘플 경고에 대한 질문 : = JDK9 : 잘못된 반사 액세스 작업이 발생했습니다. org.python.core.PySystemState

마지막으로 중요한 참고 사항은 이러한 경고에 직면하지 않고 미래의 안전을 보장하기 위해 노력하는 동안 모듈이 불법 반사 액세스를하지 않도록하는 것입니다. 🙂


답변

Java 9 모듈 시스템에 관한 Oracle 기사가 있습니다.

기본적으로 모듈의 유형은 공용 유형이 아니고 패키지를 내 보내지 않는 한 다른 모듈에서 액세스 할 수 없습니다. 노출하려는 패키지 만 노출합니다. Java 9에서는 리플렉션에도 적용됩니다.

에서 지적 OUT으로 https://stackoverflow.com/a/50251958/134894 , 사이의 차이 AccessibleObject#setAccessibleJDK8 및 JDK9에 대한이 교훈입니다. 특히 JDK9 추가

이 메서드는 다음 중 하나라도 유지되는 경우 클래스 D를 선언하는 멤버에 대한 액세스를 활성화하기 위해 클래스 C의 호출자가 사용할 수 있습니다.

  • C와 D는 같은 모듈에 있습니다.
  • 멤버는 공용이고 D는 D를 포함하는 모듈이 적어도 C를 포함하는 모듈로 내보내는 패키지에서 공용입니다.
  • 멤버는 정적으로 보호되고 D는 D를 포함하는 모듈이 최소한 C를 포함하는 모듈로 내보내는 패키지에서 공용이며 C는 D의 하위 클래스입니다.
  • D는 D를 포함하는 모듈이 적어도 C를 포함하는 모듈에 대해 열리는 패키지에 있습니다. 이름이 지정되지 않은 모듈과 열린 모듈의 모든 패키지가 모든 모듈에 대해 열려 있으므로 D가 이름이 지정되지 않은 모듈이나 열린 모듈에있을 때이 메서드는 항상 성공합니다.

모듈과 해당 내보내기의 중요성을 강조합니다 (Java 9에서).


답변

setAccessible()액세스에 사용되는 방법을 살펴보십시오.private필드와 메소드 .

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

이제이 방법이 작동하려면 더 많은 조건이 필요합니다. 거의 모든 구형 소프트웨어를 손상시키지 않는 유일한 이유는 일반 JAR에서 자동 생성 된 모듈이 매우 관대하다는 것입니다 (모든 사람을 위해 모든 것을 열고 내보내기).


답변

add-open 옵션을 사용하려면 여기에 어떤 모듈이 어떤 패키지를 제공하는지 찾는 명령이 있습니다->

java --list-modules | tr @ " " | awk '{ print $1 }' | xargs -n1 java -d

모듈 이름은 @로 표시되고 패키지 이름은 표시되지 않습니다.

참고 : JDK 11로 테스트 됨

중요 : 패키지 공급자가 불법 액세스를하지 않는 것보다 분명히 낫습니다.


답변