Byte Buddy는 Java 응용 프로그램의 런타임과 컴파일러의 도움없이 Java 클래스를 생성하고 수정하기위한 코드 생성 및 조작 라이브러리입니다. Byte Buddy는 Java 클래스 라이브러리와 함께 제공되는 코드 생성 유틸리티 외에 임의의 클래스를 생성 할 수 있으며 런타임 프록시 생성을위한 인터페이스를 구현하는 데 국한되지 않습니다. 또한 Byte Buddy는 Java 에이전트를 사용하거나 빌드 중에 수동으로 클래스를 변경하기위한 편리한 API를 제공합니다.
바이트 버디를 사용하려면 Java Byte 코드 또는 클래스 파일 형식을 이해할 필요가 없습니다. 대조적으로, 바이트 버디의 API는 모든 사람에게 간결하고 이해하기 쉬운 코드를 목표로합니다. 그럼에도 불구하고 바이트 버디는 사용자 정의 바이트 코드를 정의 할 가능성에 따라 완전히 사용자 정의 할 수 있습니다. 또한 API는 가능한 한 비 침입으로 설계되었으며 그 결과 바이트 버디는 그에 의해 만들어진 클래스에 흔적을 남기지 않습니다. 이러한 이유로, 생성 된 클래스는 클래스 경로에서 바이트 버디를 필요로하지 않고 존재할 수 있습니다. 이 기능으로 인해 바이트 버디의 마스코트는 유령으로 선정되었습니다.
바이트 버디는 Java 5로 작성되었지만 모든 Java 버전의 클래스 생성을 지원합니다. 바이트 버디는 가벼운 라이브러리이며 더 이상의 종속성이 필요하지 않은 Java Byte Code Parser Library ASM의 방문자 API에만 의존합니다.
첫눈에, 런타임 코드 생성은 피해야하는 일종의 흑 마법으로 보일 수 있으며 런타임 동안 코드를 명시 적으로 생성하는 응용 프로그램을 작성하는 개발자는 거의 없습니다. 그러나이 그림은 임의의 코드와 상호 작용 해야하는 라이브러리 및 컴파일 시간에 알려지지 않은 유형을 만들 때 변경됩니다. 이러한 맥락에서 라이브러리 구현자는 종종 사용자가 라이브러리 독점 인터페이스를 구현하거나 사용자 유형이 라이브러리에 먼저 알려지면 런타임에 코드를 생성하도록 요구하는 중 하나를 선택해야합니다. 예를 들어 Spring 또는 Hibernate 와 같은 많은 알려진 라이브러리는 평범한 오래된 Java 개체를 사용하는 용어에 따라 사용자에게 인기있는 후자의 접근법을 선택합니다. 결과적으로 코드 생성은 Java Space에서 유비쿼터스 개념이되었습니다. 바이트 버디는 코드 생성에 의존하는 사람들에게 더 나은 도구 세트를 제공하기 위해 Java 유형의 런타임 생성을 혁신하려는 시도입니다.
2015 년 10 월, Byte Buddy는 Oracle의 Duke 's Choice Award 와 구별되었습니다. 이상은 " Java 기술의 엄청난 양의 혁신 "에 대해 Byte Buddy에게 감사합니다. 우리는이 상을 수상한 것에 대해 매우 영광스럽게 생각하며 바이트 버디를 성공으로 만드는 데 도움을 준 모든 사용자와 다른 모든 사람들에게 감사의 말씀을 전합니다. 우리는 정말 감사합니다!
바이트 버디는 생산 품질에서 탁월한 성능을 제공합니다. Mockito, Hibernate, Jackson, Google의 Bazel Build System 및 기타 여러 가지와 같은 저명한 프레임 워크 및 도구에 의해 안정적이고 사용됩니다. 바이트 버디는 또한 많은 상용 제품에서 큰 결과를 얻기 위해 사용됩니다. 현재 1 년에 7 천 5 백만 회 이상 다운로드되었습니다.
바이트 버디와 함께 Hello World를 말하는 것은 가능한 한 쉽습니다. Java 클래스의 모든 생성은 새로운 유형을 만드는 구성을 나타내는 ByteBuddy
클래스의 인스턴스로 시작합니다.
Class <?> dynamicType = new ByteBuddy ()
. subclass ( Object . class )
. method ( ElementMatchers . named ( "toString" ))
. intercept ( FixedValue . value ( "Hello World!" ))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat ( dynamicType . newInstance (). toString (), is ( "Hello World!" ));
위의 예제에서 사용되는 기본 ByteBuddy
구성은 Java Virtual Machine을 처리하여 이해되는 최신 버전의 클래스 파일 형식에서 Java 클래스를 만듭니다. 예제 코드에서 희망적으로 명백히, 생성 된 유형은 Object
클래스를 확장하고 Hello World!
의 고정 값을 반환 해야하는 toString
메소드를 재정의합니다! . 재정의 방법은 소위 ElementMatcher
에 의해 식별됩니다. 위의 예에서는 정확한 이름으로 메소드를 식별하는 named(String)
사전 정의 된 요소 매칭이 사용됩니다. 바이트 버디에는 ElementMatchers
클래스에서 수집되어 쉽게 구성 할 수있는 수많은 사전 정의되고 잘 테스트 된 경기자가 함께 제공됩니다. 그러나 사용자 정의 매칭은 (기능적) ElementMatcher
인터페이스를 구현하는 것만 큼 간단합니다.
toString
메소드를 구현하기 위해 FixedValue
클래스는 재정의 메소드에 대한 상수 반환 값을 정의합니다. 일정한 값을 정의하는 것은 바이트 버디와 함께 배송되는 많은 방법 요격기의 한 예일뿐입니다. 그러나 Implementation
인터페이스를 구현함으로써 메소드는 사용자 정의 바이트 코드로 정의 될 수도 있습니다.
마지막으로, 설명 된 Java 클래스가 생성 된 다음 Java Virtual Machine에로드됩니다. 이를 위해서는 대상 클래스 로더가 필요합니다. 결국, 우리는 생성 된 클래스의 인스턴스에서 toString
메소드를 호출하고 우리가 예상 한 상수 값을 나타내는 반환 값을 찾음으로써 결과를 확신 할 수 있습니다.
물론 Hello World 예제는 코드 생성 라이브러리의 품질을 평가하기에는 너무 간단한 사용 사례입니다. 실제로, 이러한 라이브러리의 사용자는 예를 들어 Java 프로그램의 실행 경로에 후크를 도입하여보다 복잡한 조작을 수행하려고합니다. 그러나 바이트 친구를 사용하면 그렇게하는 것도 마찬가지로 간단합니다. 다음 예는 메소드 호출을 가로 채는 방법을 맛볼 수 있습니다.
바이트 버디는 Implementation
인터페이스 인스턴스에 의해 동적으로 정의 된 메소드 구현을 표현합니다. 이전 예에서는이 인터페이스를 구현하는 FixedValue
이미 시연되었습니다. 이 인터페이스를 구현함으로써 바이트 버디 사용자는 메소드에 대한 사용자 정의 바이트 코드를 정의하는 길이로 이동할 수 있습니다. 그러나 일반적으로 Plain Java에서 모든 메소드를 구현할 수있는 MethodDelegation
과 같은 Byte Buddy의 사전 정의 된 구현을 사용하는 것이 더 쉽습니다. 이 구현을 사용하는 것은 컨트롤 흐름을 모든 POJO로 위임하여 작동함에 따라 직접적으로 진행됩니다. 그러한 포조의 예로서, 바이트 버디는 예를 들어 다음 클래스의 유일한 방법으로 호출을 리디렉션 할 수 있습니다.
public class GreetingInterceptor {
public Object greet ( Object argument ) {
return "Hello from " + argument ;
}
}
위의 인사말 GreetingInterceptor
는 바이트 버디 유형에 의존하지 않습니다. 바이트 버디가 생성하는 수업 중 어느 것도 수업 경로에 바이트 버디가 필요하지 않기 때문에 좋은 소식입니다! 위의 GreetingInterceptor
고려할 때 바이트 버디를 사용하여 Java 8 java.util.function.Function
인터페이스 및 초록 apply
방법을 구현할 수 있습니다.
Class <? extends java . util . function . Function > dynamicType = new ByteBuddy ()
. subclass ( java . util . function . Function . class )
. method ( ElementMatchers . named ( "apply" ))
. intercept ( MethodDelegation . to ( new GreetingInterceptor ()))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat (( String ) dynamicType . newInstance (). apply ( "Byte Buddy" ), is ( "Hello from Byte Buddy" ));
Byte Buddy는 위의 코드를 실행하여 Java의 Function
인터페이스를 구현하고 이전에 정의한 인사말 GreetingInterceptor
pojo 인스턴스에 대한 대표단으로서의 apply
메소드를 구현합니다. 이제 Function::apply
메소드가 호출 될 때마다 제어 흐름이 GreetingInterceptor::greet
으로 발송되고 후자의 메소드의 리턴 값은 인터페이스의 메소드에서 반환됩니다.
인터셉터는 인터셉터 매개 변수에 주석을 달아보다 일반적인 입력 및 출력으로 가져갈 수 있도록 정의 할 수 있습니다. 바이트 버디가 주석을 발견하면 라이브러리는 인터셉터 매개 변수에 필요한 종속성을 주입합니다. 보다 일반적인 인터셉터의 예는 다음 클래스입니다.
public class GeneralInterceptor {
@ RuntimeType
public Object intercept ( @ AllArguments Object [] allArguments ,
@ Origin Method method ) {
// intercept any method of any signature
}
}
위의 인터셉터를 사용하면 차단 된 모든 방법이 일치하고 처리 될 수 있습니다. 예를 들어, Function::apply
할 때, 메소드의 인수는 배열의 단일 요소로 전달됩니다. 또한, Fuction::apply
에 대한 Method
참조는 @Origin
주석으로 인해 인터셉터의 두 번째 인수로 전달됩니다. 이 메소드에 @RuntimeType
주석을 선언함으로써 Byte Buddy는 필요한 경우 가로 채기 된 메소드의 리턴 값에 반환 된 값을 시전합니다. 그렇게함으로써 바이트 버디는 자동 권투 및 Unboxing을 적용합니다.
이미 언급 된 주석 외에도 다른 사전 정의 된 주석이 많이 있습니다. 예를 들어, Runnable
또는 Callable
유형에서 @SuperCall
주석을 사용할 때 바이트 버디는 그러한 방법이 존재하는 경우 비공개 슈퍼 메소드를 호출 할 수있는 프록시 인스턴스를 주입합니다. 바이트 버디가 사용 사례를 다루지 않더라도 바이트 버디는 사용자 정의 주석을 정의하기위한 확장 메커니즘을 제공합니다.
이러한 주석을 사용하면 코드가 바이트 버디와 연결될 것으로 기대할 수 있습니다. 그러나 Java는 클래스 로더에 보이지 않는 경우 주석을 무시합니다. 이런 식으로 생성 된 코드는 바이트 친구없이 존재할 수 있습니다! MethodDelegation
및 Javadoc 및 Byte Buddy 's Tutorial에서 미리 정의 된 주석에 대한 자세한 내용은 찾을 수 있습니다.
바이트 버디는 서브 클래스 생성에만 국한되지 않지만 기존 코드를 재정의 할 수도 있습니다. 이를 위해 Byte Buddy는 소위 Java 에이전트를 정의하기위한 편리한 API를 제공합니다. Java 에이전트는 런타임 동안 기존 Java 응용 프로그램의 코드를 변경하는 데 사용할 수있는 일반적인 Java 프로그램입니다. 예를 들어, 바이트 버디를 사용하여 실행 시간을 인쇄하는 방법을 변경할 수 있습니다. 이를 위해 먼저 이전 예제에서 인터셉터와 유사한 인터셉터를 정의합니다.
public class TimingInterceptor {
@ RuntimeType
public static Object intercept ( @ Origin Method method ,
@ SuperCall Callable <?> callable ) {
long start = System . currentTimeMillis ();
try {
return callable . call ();
} finally {
System . out . println ( method + " took " + ( System . currentTimeMillis () - start ));
}
}
}
Java 에이전트를 사용하여 이제이 인터셉터를 TypeDescription
의 ElementMatcher
일치하는 모든 유형에 적용 할 수 있습니다. 예를 들어, 우리는 Timed
로 끝나는 이름을 가진 모든 유형에 위의 인터셉터를 모든 유형에 추가하도록 선택합니다. 이것은 단순성을 위해 수행되는 반면, 주석은 아마도 생산 에이전트에 해당 클래스를 표시하는 데 더 적절한 대안 일 것입니다. 바이트 버디의 AgentBuilder
API를 사용하면 Java 에이전트를 생성하는 것만 큼 쉽습니다.
public class TimerAgent {
public static void premain ( String arguments ,
Instrumentation instrumentation ) {
new AgentBuilder . Default ()
. type ( ElementMatchers . nameEndsWith ( "Timed" ))
. transform (( builder , type , classLoader , module , protectionDomain ) ->
builder . method ( ElementMatchers . any ())
. intercept ( MethodDelegation . to ( TimingInterceptor . class ))
). installOn ( instrumentation );
}
}
Java의 main
방법과 유사하게, premain
방법은 재정의를 적용하는 Java 에이전트의 진입 점입니다. 하나의 인수로서, Java 에이전트는 Instrumentation
인스턴스를 수신하여 바이트 버디가 런타임 클래스 재정의를 위해 JVM의 표준 API에 연결할 수 있습니다.
이 프로그램은 TimerAgent
를 가리키는 Premain-Class
속성과 함께 매니페스트 파일과 함께 포장됩니다. 결과적인 JAR 파일은 이제 -javaagent:timingagent.jar
를 클래스 경로에 추가하는 것과 유사하게 설정하여 Java 응용 프로그램에 추가 할 수 있습니다. 에이전트가 활성화되면 Timed
로 끝나는 모든 클래스는 이제 실행 시간을 콘솔에 인쇄합니다.
Byte Buddy는 클래스 파일 형식 변경을 비활성화하고 Advice
계측을 사용하여 소위 런타임 첨부 파일을 적용 할 수 있습니다. 자세한 내용은 Advice
의 Javadoc 과 AgentBuilder
클래스를 참조하십시오. 바이트 버디는 ByteBuddy
인스턴스를 통해 또는 바이트 버디 Maven 및 Gradle 플러그인을 사용하여 Java 클래스의 명시 적 변경을 제공합니다.
바이트 버디는 포괄적 인 라이브러리이며 바이트 버디의 기능의 표면 만 긁 혔습니다. 그러나 Byte Buddy는 수업을 만드는 데 도메인 별 언어를 제공하여 사용하기 쉬운 것을 목표로합니다. 대부분의 런타임 코드 생성은 Java의 클래스 파일 형식에 대한 지식없이 읽은 코드를 작성하여 수행 할 수 있습니다. 바이트 버디에 대해 더 많이 배우려면 바이트 버디의 웹 페이지에서 그러한 튜토리얼을 찾을 수 있습니다 (중국어 번역도 있습니다).
또한 바이트 버디는 자세한 코드 내 문서와 광범위한 테스트 케이스 커버리지가 제공되어 예제 코드 역할을 할 수 있습니다. 마지막으로 위키의 바이트 버디에서 최신 기사 및 프레젠테이션 목록을 찾을 수 있습니다. 바이트 버디를 사용할 때 프로젝트 종속성 유지 관리에 대한 다음 정보를 읽으십시오.
바이트 버디의 사용은 무료이며 라이센스 구매가 필요하지 않습니다. 그러나 도서관을 최대한 활용하거나 쉬운 출발을 확보하려면 교육, 개발 시간 또는 지원 계획을 구매할 수 있습니다. 요금은 참여 범위와 지속 시간에 따라 다릅니다. 자세한 내용은 [email protected]과 연락하십시오.
바이트 버디는 TideLift에 나열되어 있습니다. 명시 적 지원을 구매하려는 정도로 바이트 버디를 사용하지 않고 일반적인 오픈 소스 커뮤니티를 지원하려는 경우 구독을 고려하십시오.
GitHub 스폰서를 통해 내 작업을 지원할 수 있습니다. 이 옵션은 간단한 결제 채널을 찾고 있으며 대가로 지원을 기대하지 않는 상업용 배우만을위한 것입니다. GitHub 스폰서를 통한 지원은 VAT 준수를 유지할 수 없습니다. 대신 직접 지원 계약을 받으십시오.
스택 오버플로 또는 바이트 버디 메일 링리스트에 대한 일반적인 질문은 질문 아카이브 역할을 할 수 있습니다. 물론 버그 보고서는 상업 계획 외부에서도 고려됩니다. 오픈 소스 프로젝트의 경우 때때로 바이트 버디를 사용하는 데 대한 확장 된 도움을받을 수 있습니다.
바이트 버디는 편집 된 Java 클래스를 읽고 쓰는 성숙하고 잘 테스트 한 라이브러리 인 ASM 위에 작성되었습니다. 고급 유형 조작을 허용하기 위해 바이트 버디는 의도적으로 ASM API를 사용자에게 노출시키고 있습니다. 물론 ASM의 직접적인 사용은 완전히 선택적으로 남아 있으며 대부분의 사용자는이를 요구하지 않을 것입니다. 이 선택은 바이트 버디 사용자가 더 높은 수준의 기능으로 제한되지 않고 필요할 때 소란없이 사용자 정의 구현을 구현할 수 있도록 만들어졌습니다.
ASM은 이전에 공개 API를 변경했지만 라이브러리 버전 4로 시작하는 API 호환성 메커니즘을 추가했습니다. 버전이 이러한 이전 버전과 충돌하지 않기 위해 바이트 버디는 ASM 의존성을 자체 네임 스페이스로 다시 포장합니다. ASM을 직접 사용하려면 byte-buddy-dep
Artifact는 ASM에 명시적인 의존성을 가진 바이트 버드 버전을 제공합니다. 그렇게 할 때는 버전 충돌을 피하기 위해 바이트 버디와 ASM을 모두 네임 스페이스로 다시 포장 해야합니다 .
이 프로젝트의 보안 정책에 유의하십시오.
바이트 버디는 버전 5에서 모든 JVM 버전에서 단일 항아리에서 실행을 지원합니다. 이는 적극적으로 업데이트되지 않은 구형 또는 알려지지 않은 응용 프로그램을 지원 해야하는 Java 에이전트의 개발을 용이하게하기 위해 수행됩니다. 이를 허용하면서 현대식 Java 및 Stack Map 프레임으로 CD 또는 클래스 검증과 같은 기능을 지원하는 동시에 버전 5 및 8의 클래스 파일을 포함하는 멀티 릴리스 항아리로 바이트 버디 선박의 주요 항아리. 결과적으로 바이트 버디의 항아리 크기는 예상대로 높습니다. 바이트 버디 클래스의 대부분은 절대로로드되지 않기 때문에 JAR 파일 크기는 일반적으로 문제가되지 않습니다. 그러나 Java 에이전트를 배포 할 때 파일 크기가 문제가 될 수 있습니다. 에이전트는 이미 단일 항아리로 묶어야 하므로이 문제를 줄이기 위해 기본 Java 5 버전 또는 포함 된 클래스 파일의 다중 방출 Java 8 버전을 제거하는 것이 좋습니다. 이것은 Maven Shade 플러그인과 같은 목적을 위해 대부분의 빌드 플러그인에서 지원됩니다.
Byte Buddy는 자유주의적이고 비즈니스 친화적 인 Apache 라이센스, 버전 2.0 에 따라 라이센스를 받았으며 Github에서 무료로 제공됩니다. 또한, 바이트-버디 분배는 3 명 BSD 라이센스에 따라 릴리스되는 ASM을 번들로 묶습니다.
바이트 버디 바이너리는 Maven Central과 Jcenter의 저장소에 출판됩니다. 아티팩트 서명은 바이트 버디 1.10.3으로 시작하는이 PGP 공개 키에 대해 검증 될 수 있습니다. 이전 버전은이 이전 버전과 약한 인증서에 대해 검증 될 수 있습니다.
이 프로젝트는 Maven을 사용하여 구축되었습니다. 껍질에서 클로닝 및 프로젝트 구축에서 다음과 같은 일이 될 것입니다.
git clone https://github.com/raphw/byte-buddy.git
cd byte-buddy
mvn package
이러한 명령에서 바이트 버디는 GitHub에서 복제되어 컴퓨터에 제작되었습니다. 추가 빌드 옵션은 루트 POM 파일에 나열되어 있습니다. 바이트 버드는 최소 6 개 이상의 JDK로 제작할 수 있습니다. 그러나 버전 6 및 7의 빌드에는 암호화되지 않은 HTTP의 사용이 필요하므로 적어도 버전 8의 JDK를 사용하는 것이 좋습니다. 지원은이 JDK 버전에 대한 테스트를 실행하기위한 것이며 중간 공격에 노출 될 수 있습니다. 따라서 이러한 빌드는 피해야합니다. 바이트 버디는 현재 CI 서버의 JDK보다 버전 6 이상을 테스트하고 있습니다.
버그보고를 위해 Github의 문제 추적기를 사용하십시오. 코드를 커밋 할 때는 기능의 기능을 입증하거나 버그 수정을 보여주는 테스트 케이스를 제공하십시오. 또한 기존 테스트 사례를 중단하지 않도록하십시오. 가능하면 시간을내어 문서를 작성하십시오. 기능 요청 또는 일반 피드백의 경우 문제 추적기를 사용하거나 메일 링리스트에서 문의 할 수도 있습니다.
바이트 버디에 대한 작업은 정기적 인 자원과 프로젝트에 대한 관심을 가진 많은 지지자들 덕분에 가능합니다. 시간을내어 지지자들과 그들의 제안을 살펴보십시오.