[구덩이 밟기 일기] 정적 도입이 Lombok의 실효를 초래하여 번역에 실패한 참사를 기록한다.
배경.
시간: 어느 평범한 월요일 날씨: 맑고 구름 한 점 없는 내용: 즐겁게 수요를 쓰고 코드를 제출하며 포장하여 배치한다.
[INFO] --------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] cannot find symbol
[ERROR] non-static method cannot be referenced from a static context
[ERROR] invalid method reference
[ERROR] cannot find symbol
[ERROR] cannot find symbol
[ERROR] cannot find symbol
[ERROR] cannot find symbol
......
자식, 갑자기 수백 개의 컴파일 오류가 터졌어. 나도 여동생이 뭘 하는지 생각해 봤어. 단지 업무 코드만 썼을 뿐이야.프로젝트Pom, Maven 사복, Jenkins 설정을 검사한 결과 모든 것이 정상적이고 이전의 지점을 치는 데 전혀 문제가 없었다.그러나 로컬과 원격에서 이 새로운 지점을 개설하면 안 된다.
삭원
환경 문제를 배제하고 나머지는 틀림없이 코드 문제일 것이다.여기서 Maven 패키지 로그를 살펴보면 오류가 컴파일링 기간에 발생했고 내용은 대체로 기호를 찾을 수 없고 방법이 존재하지 않는다는 등이다.코드 생성과 관련된 구성 요소에 문제가 생겼다고 생각하기 쉬우니, 첫 번째 타격은 Lombok에게 죄를 물어야 한다.
처음엔 롬봇 버전 충돌 같은 문제인 줄 알았는데 IDEA를 사용한 디펜던스 디그람은 이질감을 보지 못했다.설마 이 안에 구덩이가 있는 건 아니겠지?Stack Overflow에 도움을 청할 수밖에 없었는데, 아니나 다를까...알고 보니 이것은 오래된 BUG였고, Lombok 개발팀은 복구할 의사가 없었다.
원흉
긴 말은 짧게 하자면, 원흉은 바로 프로젝트 중의 정적 도입이다!우리는 간단하게 재현할 수 있다.
package com.mycompany.lombokutilityclassbug;
@UtilityClass
public class MyUtilityClass {
void foo() { System.out.println ("hi"); }
}
여기에 @UtilityClass 주석을 추가하여 구성원 방법에 static 수식자를 자동으로 추가하는 클래스를 만듭니다.
package com.mycompany.lombokutilityclassbug;
import static com.mycompany.lombokutilityclassbug.MyUtilityClass.foo;
public class Main { }
그리고 다른 곳에서 이 도구 클래스의 다음 클래스 방법을 정적으로 가져옵니다.
지정한 클래스 구성원을 정적 가져오는 것이지, 어댑터를 사용해야만 오류를 재현할 수 있습니다.즉 어댑터를 사용하는 것은 아무런 문제가 없다!
import static com.mycompany.lombokutilityclassbug.MyUtilityClass.*; //works pretty fine.
import static com.mycompany.lombokutilityclassbug.MyUtilityClass.foo; //broken
그리고 한 파를 번역하면 본문의 첫머리와 같은 오류를 볼 수 있다.
그러나 이상하게도 개발할 때 Idea가 틀리지 않고 파란만장하다. 오류는 컴파일할 때만 발생한다.
Why?
다음은 Lombok 개발팀의 맏형을 발췌한 대목입니다.
This is a known bug, and not something that's easy to fix. Static imports are resolved before the annotation processors are run. This is a problem in javac, not lombok.
간단히 설명하자면, 이것은 이미 알고 있는 버그이며, 복구하기가 매우 어렵다.정적 가져오기는 주해 프로세서 (annotaion processors) 가 실행되기 전에 컴파일러에 의해 해석됩니다. 이것은 javac의 문제입니다. 이 솥은 멜지 않습니다.
주석 처리기
그러면 메모 프로세서(Annotation Processor)는 무엇입니까?자신의 주석을 정의할 때 자바에서 미리 설정한 모듈 주석을 사용해야 합니다. 그 중 @Retention이라는 것이 있습니다. 이 주석의 보존 주기를 설명하는 데 사용되며, RetentionPolicy 매거를 받아들일 수 있습니다.
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
기본 동작은 CLASS 레벨을 선택하는 것입니다. 이것은 주석이 컴파일 기간
class
파일에 남아 있지만 실행할 때 JVM 메모리에 불러오지 않는다는 것을 의미합니다.RUNTIME 레벨
class
파일과 JVM에 동시에 보존되며 반사 구성 요소가 스캔할 수 있고 스캔할 수 있는 주석이 바로 이런 종류입니다.SOURCE 레벨에서 주석은 컴파일링 기간에 버려지고 CLASS보다 개발 보조와 코드 생성에 더 많이 사용된다.
가시도: SOURCE
예를 들어
@Override
는 SOURCE 주석이다. 현대 IDE는 부류를 덮어쓰지 않은 방법을 식별할 때@Override
주석을 칠 때 오류 표시를 할 수 있는데 그 원리는 주석 프로세서이다.Lombok의 모든 주석은 SOURCE로 보존됩니다. 주석 프로세서를 사용하여 템플릿 코드를 생성하기 때문입니다.
상술한 개념을 결합하면 왜 이 냄비 녀석이 우리를 업지 않는지 약간의 갈피를 잡을 수 있을 것 같다.자바 컴파일링 과정에 일종의 시차 문제가 존재하는 것 같습니다. 다음은 컴파일러의 작업 시차에 대해 대담한 추측을 해보겠습니다.
이런 시퀀스에서 정적 도입 문장의 컴파일링은 주해 프로세서의 운행보다 먼저 실행된다. 이때 템플릿 코드가 생성되지 않았을 때 당연히 컴파일 오류를 보고할 것이다.
그런데 왜 IDE에서 이런 오류를 보고하지 않았을까요?IDEA에서 Lombok을 사용하려면 특정 플러그인을 설치해야 합니다.결과적으로 그 컴파일 과정은 원생 JDK와 다르다. 그 중에서 일부 개입과 최적화가 존재하여 오류가 덮였을지도 모른다.
어떻게 피할 것인가
정부측에서 이미 손을 떼고 고치지 않은 것을 감안하면, 고칠 수도 없다.우리는 개발 코드를 수정하여 문제를 피할 수 밖에 없다. 이런 문제에 대해 우리는 하나의 통해(java공구서 스타일)를 정리한다.
정적 가져오기를 사용할 때, 가져온 코드 요소가 주석 프로세서를 통해 동적으로 생성된 경우, 구명 정적 가져오기를 사용하지 말고 어댑터를 사용할 수 있습니다.IDE는 일반적으로 가져오기 문장을 자동으로 최적화하기 때문에, 이 컴파일러 단계의 빈틈을 보고하지 않고, 어댑터를 구명으로 줄일 수 있습니다. 최선의 방법은 두 가지를 동시에 사용하지 않는 것입니다.
What's More
Lombok의 시차 문제에 관하여 Lombok 작가는 위키:Lombok 개념:해석, 관심 있는 학우들이 연구할 수 있는 한 편을 썼다.저자는 이를'닭이 먼저냐, 알이 먼저냐의 문제'에 비유한다.그 배후의 모순을 이해한 후에 Lombok의 일부 한계성을 설명할 수 있다. 예를 들어 Builder/AllArgsConstructor/...왜 부류 필드를 계승할 수 없습니까?
참조:
[1] static import not working in lombok builder in intelliJ - (2017/12/06) https://stackoverflow.com/questions/47674264/static-import-not-working-in-lombok-builder-in-intellij
[2] @Builder not work with static import in Intellij 2016.2.4 - (2016/09/27) https://github.com/mplushnikov/lombok-intellij-plugin/issues/291
[3] JEP 216: Process Import Statements Correctly - (2014/08/26) http://openjdk.java.net/jeps/216
[4] LOMBOK CONCEPT: Resolution - (2018/06/12) https://github.com/rzwitserloot/lombok/wiki/LOMBOK-CONCEPT:-Resolution
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.