java.lang.instrument Agent ใช้งาน
แพ็คเกจ java.lang.instrument ได้รับการแนะนำใน JDK5 โปรแกรมเมอร์สามารถปรับเปลี่ยนรหัสคลาสแบบไดนามิกโดยการแก้ไข bytecode ของวิธีการ โดยปกติจะถูกประมวลผลล่วงหน้าก่อนที่จะมีวิธีการหลักของชั้นเรียนและใช้งานโดย Java เพื่อระบุคลาสพร็อกซีของคลาส ก่อนที่รหัส bytecode ของคลาสจะถูกโหลดลงใน JVM วิธีการแปลงของ classfiletransformer จะถูกเรียกให้ตระหนักถึงฟังก์ชั่นของการปรับเปลี่ยนวิธีคลาสดั้งเดิมและใช้ AOP ข้อดีของสิ่งนี้คือมันจะไม่สร้างคลาสใหม่เช่นเทคโนโลยีพร็อกซีแบบไดนามิกหรือเทคโนโลยี CGLIB ที่ใช้ AOP และไม่จำเป็นต้องให้คลาสดั้งเดิมมีอินเทอร์เฟซ
(1) เอเจนต์เป็นตัวดักจับก่อนวิธีการหลักของคุณนั่นคือรหัสที่เรียกใช้งานเอเจนต์ก่อนที่วิธีการหลักจะถูกดำเนินการ รหัสของเอเจนต์ทำงานใน JVM เดียวกับวิธีการหลักของคุณถูกโหลดโดยระบบคลาสเดียวกันและได้รับการจัดการโดยนโยบายและบริบทความปลอดภัยเดียวกัน เอเจนต์ชื่อค่อนข้างทำให้เข้าใจผิดเล็กน้อยและมันก็ไม่เหมือนกับตัวแทนที่เราเข้าใจโดยทั่วไป ตัวแทน Java นั้นใช้งานง่าย จะเขียนตัวแทน Java ได้อย่างไร? คุณจะต้องใช้วิธีการ premain: Public Static Void Premain (String Agentargs, Instrumentation Inst) หากคำจำกัดความข้างต้นของ Premain ไม่สามารถพบได้ใน JDK 6 คุณจะพยายามเรียกคำจำกัดความของ Premain ต่อไปนี้
(2) คลาสเอเจนต์จะต้องพิมพ์ลงในแพ็คเกจ JAR จากนั้น meta-inf/mainifest.mf ภายในจะต้องมีแอตทริบิวต์ premain-class นี่คือตัวอย่างของ manifest.mf:
Manifest-Version: 1.0 Premain-Class: MyAgent1 สร้างขึ้นโดย: 1.6.0_06
จากนั้นเพิ่ม manifest.mf ลงในแพ็คเกจขวดของคุณ ต่อไปนี้เป็นแอตทริบิวต์รายการ Manifest สำหรับไฟล์ jar ตัวแทน: premain-class หากมีการระบุพร็อกซีเมื่อเริ่มต้น JVM แอตทริบิวต์นี้จะระบุคลาสพร็อกซีนั่นคือคลาสที่มีวิธีการก่อนกำหนด คุณสมบัตินี้เป็นสิ่งจำเป็นหากมีการระบุพร็อกซีเมื่อเริ่มต้น JVM หากไม่มีคุณสมบัติ JVM จะยกเลิก หมายเหตุ: คุณสมบัตินี้เป็นชื่อคลาสไม่ใช่ชื่อไฟล์หรือเส้นทาง Agent-class หากการใช้งานสนับสนุนกลไกในการเริ่มต้นตัวแทนในช่วงเวลาหนึ่งหลังจาก VM เริ่มต้นคุณสมบัตินี้จะระบุคลาสตัวแทน นั่นคือคลาสที่มีวิธี AgentMain คุณสมบัตินี้เป็นสิ่งจำเป็นและพร็อกซีจะไม่เริ่มต้นหากไม่มีอยู่จริง หมายเหตุ: นี่คือชื่อคลาสไม่ใช่ชื่อไฟล์หรือเส้นทาง Boot-Class-Path ตั้งค่ารายการพา ธ สำหรับการค้นหาตัวโหลดคลาสบูต เส้นทางแสดงถึงไดเรกทอรีหรือไลบรารี (มักจะอ้างอิงเป็นห้องสมุด JAR หรือ ZIP ในหลายแพลตฟอร์ม) หลังจากกลไกเฉพาะแพลตฟอร์มสำหรับการค้นหาคลาสล้มเหลวตัวโหลดคลาสบูตจะค้นหาเส้นทางเหล่านี้ ค้นหาเส้นทางในลำดับที่แสดงรายการ เส้นทางในรายการจะถูกคั่นด้วยช่องว่างอย่างน้อยหนึ่งช่อง พา ธ ใช้ไวยากรณ์ส่วนประกอบพา ธ ของ URI แบบลำดับชั้น หากเส้นทางเริ่มต้นด้วยอักขระสแลช ("/") มันเป็นเส้นทางที่แน่นอนมิฉะนั้นจะเป็นเส้นทางที่สัมพันธ์กัน เส้นทางสัมพัทธ์จะถูกแยกวิเคราะห์ขึ้นอยู่กับเส้นทางสัมบูรณ์ของไฟล์พร็อกซีขวด ละเว้นเส้นทางที่มีรูปแบบที่ไม่ถูกต้องและเส้นทางที่ไม่มีอยู่จริง หากเอเจนต์เริ่มต้นในช่วงเวลาหนึ่งหลังจากที่ VM เริ่มต้นเส้นทางที่ไม่ได้เป็นตัวแทนไฟล์ JAR จะถูกละเว้น คุณสมบัตินี้เป็นทางเลือก บูลีนแบบ can-redefine-classes (จริงหรือเท็จไม่เกี่ยวข้องกับตัวพิมพ์ใหญ่บนและล่าง) ไม่ว่าจะเป็นคลาสที่จำเป็นสำหรับพร็อกซีนี้หรือไม่ ค่าอื่นนอกเหนือจากความจริงถือเป็นเท็จ คุณสมบัตินี้เป็นทางเลือกและค่าเริ่มต้นเป็นเท็จ บูลีนแบบ retransform-classes (จริงหรือเท็จไม่เกี่ยวข้องกับตัวพิมพ์ใหญ่บนและล่าง) ไม่ว่าจะเป็นคลาสที่จำเป็นสำหรับพร็อกซีนี้หรือไม่ ค่าอื่นนอกเหนือจากความจริงถือเป็นเท็จ คุณสมบัตินี้เป็นทางเลือกและค่าเริ่มต้นเป็นเท็จ สามารถตั้งค่าบูลีนแบบเมธอด-prefix (จริงหรือเท็จไม่เกี่ยวข้องกับตัวพิมพ์ใหญ่บนและล่าง) ไม่ว่าจะเป็นคำนำหน้าวิธีการดั้งเดิมที่กำหนดโดยพร็อกซีนี้หรือไม่ ค่าอื่นนอกเหนือจากความจริงถือเป็นเท็จ คุณสมบัตินี้เป็นทางเลือกและค่าเริ่มต้นเป็นเท็จ
(3) แพ็คเกจ Jar ตัวแทนเหล่านี้ทั้งหมดจะถูกเพิ่มลงใน ClassPath ของโปรแกรมโดยอัตโนมัติ ดังนั้นจึงไม่จำเป็นต้องเพิ่มพวกเขาลงใน ClassPath ด้วยตนเอง เว้นแต่คุณต้องการระบุลำดับของ ClassPaths
(4) ไม่มีการ จำกัด จำนวนพารามิเตอร์ของ -javaagent ในโปรแกรม Java ดังนั้นคุณสามารถเพิ่มตัวแทน Java ได้มาก ตัวแทน Java ทั้งหมดจะถูกดำเนินการตามลำดับที่คุณกำหนด ตัวอย่างเช่น:
java -javaagent: myagent1.jar -javaagent: myagent2.jar -jar myprogram.jar
สมมติว่าฟังก์ชั่นหลักใน myProgram.jar อยู่ใน myProgram myagent1.jar, myagent2.jar, คลาสที่ใช้ premain ในแพ็คเกจ Jar สองชุดนี้คือ MyAgent1 และลำดับการดำเนินการของโปรแกรม MyAgent2 จะเป็น:
myagent1.premain -> myagent2.premain -> myprogram.main
(5) นอกจากนี้ Premain วางหลังจากฟังก์ชั่นหลักจะไม่ถูกดำเนินการตัวอย่างเช่น:
java -javaagent: myagent1.jar -jar myprogram.jar -javaagent: myagent2.jar
MyAgent2 อยู่ด้านหลัง myProgram.jar ดังนั้น Premain of MyAgent2 จะไม่ถูกดำเนินการดังนั้นผลการดำเนินการจะเป็น:
myagent1.premain -> myprogram.main
(6) ตัวแทน Java แต่ละตัวสามารถรับพารามิเตอร์ประเภทสตริงได้นั่นคือ AgentArgs ใน Premain Agentargs นี้ถูกกำหนดไว้ในตัวเลือก Java ตัวอย่างเช่น:
java -javaagent: myagent2.jar = thisisagentargs -jar myprogram.jar
มูลค่าของ AgentArgs ที่ได้รับจาก Premain ใน MyAgent2 จะเป็น "ThisisagentArgs" (ไม่รวมคำพูดสองครั้ง)
(7) เครื่องมือในพารามิเตอร์: เพิ่ม classFileTransformer ที่กำหนดโดยพารามิเตอร์เพื่อเปลี่ยนไฟล์คลาส หม้อแปลงที่กำหนดเองที่นี่ใช้วิธีการแปลงซึ่งให้การปรับเปลี่ยนไปยังรหัส bytecode ของคลาสที่จะดำเนินการจริงและยังสามารถไปถึงจุดของการดำเนินการวิธีการเรียนอื่น ตัวอย่างเช่น: การเขียน Agent Class:
แพ็คเกจ org.toy; นำเข้า java.lang.instrument.instrumentation; นำเข้า java.lang.instrument.classFileTransformer; ชั้นเรียนสาธารณะ perfmonagent {เครื่องมือคงที่ส่วนตัว Inst = null; /** * วิธีนี้เรียกว่าก่อนวิธีการหลักของแอปพลิเคชันถูกเรียกว่า * เมื่อเอเจนต์นี้ถูกระบุไว้ใน Java VM **/ โมฆะคงที่สาธารณะ premain (สตริง agentargs, เครื่องมือ _inst) {system.out.println ("perfmonagent.premain () ถูกเรียกว่า"); // เริ่มต้นตัวแปรคงที่ที่เราใช้ในการติดตามข้อมูล Inst = _inst; // ตั้งค่าหม้อแปลงไฟล์คลาส classFileTransformer trans = new perfmonxformer (); System.out.println ("การเพิ่มอินสแตนซ์ perfmonxformer ลงใน JVM"); Inst.AddTransformer (trans); -เขียนคลาส classfiletransformer:
แพ็คเกจ org.toy; นำเข้า java.lang.instrument.classFileTransformer; นำเข้า java.lang.instrument.illegalclassformatexception; นำเข้า Java.security.protectionDomain; นำเข้า Javassist.cannotCompileLeException; javassist.notfoundexception; นำเข้า Javassist.expr.expreditor; นำเข้า Javassist.expr.methodcall; คลาสสาธารณะ Perfmonxformer ใช้ classfiletransformer {public byte [] transforment (classloader loader, classname คลาส ไบต์ [] transformed = null; System.out.println ("transforming" + classname); classpool pool = classpool.getdefault (); ctclass cl = null; ลอง {cl = pool.makeclass (ใหม่ java.io.byTearrayInputStream (ClassFileBuffer)); if (cl.isinterface () == false) {ctbehavior [] methods = cl.getdeclaredbehaviors (); สำหรับ (int i = 0; i <methods.length; i ++) {ถ้า (วิธีการ [i] .isempty () == false) {domethod (วิธีการ [i]); }} transformed = cl.tobytecode (); }} catch (exception e) {system.err.println ("ไม่สามารถใช้เครื่องมือ" + classname + ", ข้อยกเว้น:" + e.getMessage ()); } ในที่สุด {ถ้า (cl! = null) {cl.detach (); }} return transformed; } โมฆะส่วนตัว domethod (วิธีการ ctbehavior) โยน notfoundexception ไม่สามารถคอมไพล์เอ็กเซ็ท {// method.insertbefore ("stime ยาว = system.nanotime ();"); // method.insertafter ("system.out.println (/" leave "+method.getName ()+" และเวลา:/"+(system.nanotime ()-otmet));"); Method.Instrument (ใหม่ expreditor () {public void แก้ไข (methodCall m) การโยนไม่สามารถคอมไพล์เอ็กซ์ {m.replace ("{stime ยาว = system.nanotime (); $ _ = $ ดำเนินการ ($$); system.out.println (/" +M.GetClassName () +" +" ":/"+(system.nanotime ()-stime));} ");}}); - -สองคลาสข้างต้นเป็นแกนกลางของตัวแทน เมื่อ JVM เริ่มต้น perfmonagent.premain จะถูกเรียกก่อนที่แอปพลิเคชันจะถูกโหลด จากนั้น classfiletransforme ที่กำหนดเองคือ perfmonxformer ได้รับการสร้างอินสแตนซ์ใน perfmonagent.premain จากนั้น classfiletransformer ที่กำหนดเองจะถูกสร้างอินสแตนซ์ใน perfmonxformer จากนั้นอินสแตนซ์ของ perfmonxformer จะถูกเพิ่มเข้าไปในอินสแตนซ์เครื่องมือวัด (ส่งจาก JVM) สิ่งนี้ทำให้ PerfMonxformer.Transform จะถูกเรียกเมื่อคลาสในแอปพลิเคชันถูกโหลด คุณสามารถเปลี่ยนคลาสที่โหลดในวิธีนี้ มันวิเศษจริงๆ เพื่อที่จะเปลี่ยน bytecode ของชั้นเรียนฉันใช้ JBoss 'Javassist แม้ว่าคุณจะไม่ต้องใช้มันเช่นนี้ JBoss 'Javassist นั้นทรงพลังจริงๆช่วยให้คุณสามารถเปลี่ยนไบต์ของคลาสได้อย่างง่ายดาย
ในวิธีการข้างต้นฉันเปลี่ยนรหัสของคลาสและเพิ่ม stime ยาว = system.nanotime (); ไปยังวิธีการทางเข้าของแต่ละคลาสและเพิ่ม System.out.println ("MethodClassName.Methodname:"+(System.Nanotime ()-Stime));
ขอบคุณสำหรับการอ่านฉันหวังว่ามันจะช่วยคุณได้ ขอบคุณสำหรับการสนับสนุนเว็บไซต์นี้!