java.lang.intrument 에이전트 사용
java.lang.instrument 패키지는 JDK5에 소개되었습니다. 프로그래머는 메소드의 바이트 코드를 수정하여 클래스 코드를 동적으로 수정할 수 있습니다. 이것은 일반적으로 클래스의 주요 방법이 호출되기 전에 전처리되며, 클래스의 프록시 클래스를 지정하기 위해 Java에 의해 구현됩니다. 클래스의 바이트 코드가 JVM에로드되기 전에 Classfiletransformer의 변환 방법을 호출하여 원래 클래스 방법을 수정하고 AOP 구현 기능을 실현합니다. 이것의 장점은 Dynamic Proxy 또는 CGLIB 기술과 같은 새로운 클래스를 생성하지 않을 것이며, 원래 클래스에 인터페이스를 가질 필요가 없다는 것입니다.
(1) 에이전트는 주요 방법, 즉 기본 방법이 실행되기 전에 에이전트를 실행하는 코드 이전의 인터셉터입니다. 에이전트의 코드는 주요 방법과 동일한 JVM에서 실행되며 동일한 시스템 클래스 로더에 의해로드되며 동일한 보안 정책 및 컨텍스트에 의해 관리됩니다. 이름 에이전트는 약간 오해의 소지가 있으며, 우리가 일반적으로 이해하는 에이전트와 크게 다릅니다. Java 에이전트는 사용하기가 비교적 간단합니다. 자바 에이전트를 작성하는 방법? Premain 방법 만 구현하면됩니다 : Public Static Void Premain (String Agentargs, Instrumentation Inst) 위의 Premain 정의를 JDK 6에서 찾을 수없는 경우 다음 Premain 정의를 호출하려고합니다. Public Static Void Premain (String Agentargs).
(2) 에이전트 클래스는 JAR 패키지에 입력해야하며 Meta-Inf/Mainifest.mf 내부에는 프리 메인 클래스 속성이 포함되어야합니다. 다음은 manifest.mf의 예입니다.
Manifest-Version : 1.0 Premain-Class : Myagent1 생성 : 1.6.0_06
그런 다음 JAR 패키지에 Manifest.mf를 추가하십시오. 다음은 에이전트 JAR 파일에 대한 명백한 속성입니다. premain 클래스 JVM이 시작될 때 프록시가 지정되면이 속성은 프록시 클래스, 즉 Premain 메소드를 포함하는 클래스를 지정합니다. JVM이 시작될 때 프록시가 지정된 경우이 속성이 필요합니다. 속성이 존재하지 않으면 JVM이 중단됩니다. 참고 :이 속성은 파일 이름이나 경로가 아닌 클래스 이름입니다. 에이전트 클래스 구현이 VM이 시작된 후 특정 순간에 에이전트를 시작하는 메커니즘을 지원하는 경우이 속성은 에이전트 클래스를 지정합니다. 즉, AgerMain 방법을 포함하는 클래스입니다. 이 속성은 필요하며 프록시가 존재하지 않으면 프록시가 시작되지 않습니다. 참고 : 이것은 파일 이름이나 경로가 아닌 클래스 이름입니다. 부트 클래스 경로는 부팅 클래스 로더 검색의 경로 목록을 설정합니다. 경로는 디렉토리 또는 라이브러리를 나타냅니다 (일반적으로 많은 플랫폼에서 JAR 또는 지퍼 라이브러리로 참조). 클래스를 찾기위한 플랫폼 별 메커니즘이 실패한 후 부팅 클래스 로더는 이러한 경로를 검색합니다. 나열된 순서의 경로를 검색하십시오. 목록의 경로는 하나 이상의 공간으로 분리됩니다. 경로는 계층 적 URI의 경로 구성 요소 구문을 사용합니다. 경로가 슬래시 문자 ( "/")로 시작하면 절대 경로이며, 그렇지 않으면 상대 경로입니다. 상대 경로는 프록시 JAR 파일의 절대 경로에 따라 구문 분석됩니다. 잘못된 형식과 존재하지 않는 경로를 가진 경로를 무시하십시오. VM이 시작된 후 특정 순간에 에이전트가 시작되면 JAR 파일을 나타내지 않는 경로는 무시됩니다. 이 속성은 선택 사항입니다. can-redefine-classes boolean (진실 또는 거짓, 어퍼 케이스와 소문자와 관련이 없습니다). 이 프록시에 필요한 클래스를 재정의 할 수 있는지 여부. True 이외의 값은 False로 간주됩니다. 이 속성은 선택 사항이며 기본값은 False입니다. Can-Retransform-Classes 부울 (진실 또는 거짓, 어퍼 케이스 및 소문자와 관련이 없습니다). 이 프록시에 필요한 클래스를 다시 구성 할 수 있는지 여부. True 이외의 값은 False로 간주됩니다. 이 속성은 선택 사항이며 기본값은 False입니다. Can-set-native-method-prefix boolean value (상류 및 소문자와 관련이없는 참 또는 거짓). 이 프록시에 필요한 기본 메소드 접두사가 설정 될 수 있는지 여부. True 이외의 값은 False로 간주됩니다. 이 속성은 선택 사항이며 기본값은 False입니다.
(3)이 모든 에이전트 JAR 패키지는 프로그램의 클래스 경로에 자동으로 추가됩니다. 따라서 클래스 경로에 수동으로 추가 할 필요가 없습니다. 클래스 경로의 순서를 지정하지 않는 한.
(4) Java 프로그램에서 -javaagent의 매개 변수 수에는 제한이 없으므로 많은 Java 에이전트를 추가 할 수 있습니다. 모든 Java 에이전트는 귀하가 정의하는 순서대로 실행됩니다. 예를 들어:
java -javaagent : myagent1.jar -javaagent : myagent2.jar -jar myprogram.jar
MyProgram.jar의 주요 함수가 MyProgram에 있다고 가정하십시오. myagent1.jar, myagent2.jar,이 두 개의 JAR 패키지에서 Premain을 구현하는 클래스는 myagent1이며 myagent2 프로그램의 실행 순서는 다음과 같습니다.
myagent1.premain-> myagent2.premain-> myProgram.Main
(5) 또한 메인 기능 이후에 배치 된 프리 메인은 예를 들어 다음과 같습니다.
java -javaagent : myagent1.jar -jar myprogram.jar -javaagent : myagent2.jar
MyAgent2는 myProgram.jar 뒤에 배치되므로 MyAgent2의 Premain은 실행되지 않으므로 실행 결과는 다음과 같습니다.
myagent1.premain-> myProgram.Main
(6) 각 Java 에이전트는 문자열 유형 매개 변수, 즉 Premain에서 Agentargs를받을 수 있습니다. 이 agentargs는 Java 옵션으로 정의됩니다. 예를 들어:
java -javaagent : myagent2.jar = thisisagentargs -jar myprogram.jar
MyAgent2에서 Premain이받은 Agentargs의 가치는 "Thisagentargs"(이중 인용문 제외)입니다.
(7) 매개 변수의 계측 : 클래스 파일을 변경하기 위해 매개 변수에 의해 정의 된 classfiletransformer를 추가하십시오. 여기에서 사용자 정의 변압기는 변환 방법을 구현하는데, 이는 실제로 실행될 클래스의 바이트 코드에 대한 수정을 제공하며 다른 클래스 방법을 실행하는 지점에 도달 할 수도 있습니다. 예를 들면 : 쓰기 에이전트 클래스 :
패키지 org.toy; import java.lang.instrument.instrumentation; import java.lang.instrument.classfiletransformer; public class perfmonagent {private static instrumentation inst = null; /** *이 메소드는 응용 프로그램의 메인 메드가 호출되기 전에 호출됩니다. *이 에이전트가 Java VM에 지정되면. **/ public static void premain (String agentargs, Instrumentation _inst) {system.out.println ( "perfmonagent.premain ()이 호출되었습니다."); // 정보를 추적하는 데 사용하는 정적 변수를 초기화합니다. inst = _inst; // 클래스 파일 변압기를 설정합니다. classFiletransformer trans = new perfmonxformer (); System.out.println ( "JVM에 PerfMonxFormer 인스턴스 추가"); inst.addtransformer (trans); }}Classfiletransformer 클래스 작성 :
패키지 org.toy; import java.lang.instrument.classfiletransformer; import java.lang.instrument.ilegalclastformatexception; import java.security.protectiondomain; import javassist.cannotcompileException; import javassist.classpool; import javassist.ct javassist javassist.notfoundexception; import javassist.expr.expreditor; import javassist.expr.methodcall; public class perfmonxformer는 classfiletransformer {public byte [] transform (classloader loader, String Classname, classname, classname, protection theportomain, classfilebofer). {byte [] transformed = null; System.out.println ( "변환" + classname); classpool pool = classpool.getDefault (); ctclass cl = null; try {cl = pool.makeclass (new java.io.bytearrayinputstream (classfilebuffer)); if (cl.isinterface () == false) {ctbehavior [] methods = cl.getDeclaredBehaviors (); for (int i = 0; i <methods.length; i ++) {if (method [i] .isempty () == false) {domethod (method [i]); }} 변환 = cl.tobytecode (); }} catch (예외 e) {system.err.println ( "incistment" + classname + ", 예외 :" + e.getMessage ()); } 마침내 {if (cl! = null) {cl.detach (); }} 리턴 변환; } private void domethod (ctbehavior method)는 notFoundException을 던지고 canclecompileException {// method.insertbefore ( "long stime = system.nanoTime ();"); // method.insertAfter ( "system.out.println (/"leave "+method.getName ()+"및 time :/"+(System.NanOtime ()-stime));"); method.instrument (new expreditor () {public void edit (methodCall m) 던지기 canconcompileException {m.replace ( "{long stime = system.nanotime (); $ _ = $ rader ($$); System.out.println (/" " +M.GetClassName () +". " +M.GETMETHODNAME () +. ":/"+(System.NanoTime () -stime));} ");}}); }}); }}위의 두 클래스는 에이전트의 핵심입니다. JVM이 시작되면 Perfmonagent.premain이 응용 프로그램이로드되기 전에 호출됩니다. 그런 다음 Customized Classfiletransforme, 즉 Perfmonxformer가 Perfmonagent.premain에 인스턴스화 된 다음 Custom Classfiletransformer가 Perfmonxformer에 인스턴스화 된 다음 Perfmonxformer 인스턴스가 계측 인스턴스 (JVM에서 전송)에 추가됩니다. 이렇게하면 Application의 클래스가로드되면 perfmonxformer.transform이 호출됩니다. 이 방법에서로드 클래스를 변경할 수 있습니다. 정말 마법입니다. 클래스의 바이트 코드를 변경하기 위해 Jboss 'javassist를 사용했습니다. 이와 같이 사용할 필요는 없지만 Jboss의 Javassist는 실제로 강력하여 클래스의 바이트 코드를 쉽게 변경할 수 있습니다.
위의 방법에서 클래스의 바이트 코드를 변경하고 긴 스티임 = System.nanoTime (); 각 클래스의 메소드 입구 및 추가 System.out.println ( "MethodClassName.MethodName :"+(System.NanoTime () -stime));
읽어 주셔서 감사합니다. 도움이되기를 바랍니다. 이 사이트를 지원 해주셔서 감사합니다!