1. Beberapa konsep dasar
Sebelum kita mulai, kita perlu menyatakan hal yang penting: kita tidak membahas anotasi yang diproses melalui mekanisme refleksi saat runtime, tetapi membahas anotasi yang diproses pada waktu kompilasi.
Apa perbedaan antara anotasi waktu kompilasi dan anotasi run-time? Bahkan, itu tidak besar, terutama karena masalah kinerja. Anotasi runtime terutama bergantung sepenuhnya pada refleksi, dan efisiensi refleksi lebih lambat daripada yang asli, sehingga akan ada beberapa kelambatan pada mesin dengan lebih sedikit memori dan CPU yang buruk. Namun, anotasi tidak akan memiliki masalah ini sama sekali, karena dalam proses kompilasi kami (kelas java->), beberapa tanda anotasi digunakan untuk secara dinamis menghasilkan beberapa kelas atau file, sehingga tidak ada hubungannya dengan operasi APK kami, sehingga secara alami tidak ada masalah kinerja. Oleh karena itu, jika proyek open source yang lebih terkenal menggunakan fungsi anotasi, biasanya menggunakan anotasi waktu kompilasi.
Prosesor Anotasi adalah alat yang dilengkapi dengan Javac, yang digunakan untuk memindai dan memproses informasi anotasi selama periode kompilasi. Anda dapat mendaftarkan prosesor anotasi Anda sendiri untuk anotasi tertentu. Di sini, saya berasumsi Anda sudah mengerti apa itu anotasi dan bagaimana menyesuaikannya. Jika Anda belum memahami anotasi, Anda dapat memeriksa dokumentasi resmi. Prosesor anotasi sudah ada di Java 5, tetapi tidak ada API yang tersedia sampai Java 6 (dirilis pada Desember 2006). Butuh beberapa saat sebelum pengguna Java menyadari kekuatan prosesor anotasi. Jadi itu hanya menjadi populer dalam beberapa tahun terakhir.
Prosesor dengan anotasi spesifik mengambil kode sumber Java (atau kompilasi bytecode) sebagai input dan kemudian menghasilkan beberapa file (biasanya file .java) sebagai output. Maksudnya itu apa? Anda dapat menghasilkan kode Java! Kode Java ini ada dalam file .java yang dihasilkan. Oleh karena itu Anda tidak dapat mengubah kelas Java yang ada, seperti menambahkan metode. File Java yang dihasilkan ini akan dikompilasi oleh Javac seperti kode sumber Java yang ditulis secara manual lainnya.
Pemrosesan anotasi dieksekusi dalam tahap kompilasi. Prinsipnya adalah membaca kode sumber Java, menguraikan anotasi, dan kemudian menghasilkan kode Java baru. Kode Java yang baru dihasilkan akhirnya dikompilasi ke dalam Java Bytecode. Prosesor anotasi tidak dapat mengubah kelas Java yang dibaca, seperti menambahkan atau menghapus metode java.
2. Abstrak Processor
Mari kita lihat API prosesor. Semua prosesor mewarisi pemrosesan abstrak, seperti yang ditunjukkan di bawah ini:
Paket com.example; import java.util.linkedhashset; impor java.util.set; impor javax.annotation.processing.abstractprocessor; impor javax.annotation.processing.processingenvironment; impor javax.annotation.processing.roundenonvent; Veronon; javax.annotation.processing.supportedAnnotationTypes; impor javax.annotation.processing.supportedSourceversion; impor javax.lang.model.sourceversion; Impor javax.Lang.model.element.typeelement; Kelas publik Myprocessorer Extendsoror.model.element.typeelement; Kelas publik Myprocororsorsorsorsorsorsorororororororer {element.typeelement; kelas publik Myprocororsorsorsorsorsorsorer.model.element.typeelement; kelas publik myprocororsorsorsorsorororororororororororororor @ Typeelement> pengumuman, Roundenvironment env) {return false; } @Override Public Set <String> GetSupportedAnnotationTypes () {set <string> annotatasions = new LinkedHashSet <String> (); annotataions.add ("com.example.myannotation"); mengembalikan annotataon; } @Override Public Sourceversion GetSupportedSourCeversion () {return Sourceversion.latestSupported (); } @Override public disinkronkan void init (ProcessingEnvironment ProcessingEnv) {super.init (ProcessingEnv); }}Init (ProcessingEnvironment ProcessingEnv): Semua kelas prosesor anotasi harus memiliki konstruktor tanpa parameter. Namun, ada metode khusus init (), yang dipanggil oleh alat pemrosesan anotasi, mengambil Lingkungan ProcessEnt sebagai parameter. ProcessingEnvironment menyediakan beberapa elemen kelas alat praktis, jenis dan pelapor. Kami akan menggunakannya nanti.
process(Set<? extends TypeElement> annoations, RoundEnvironment env) : Ini mirip dengan metode utama () dari setiap prosesor. Anda dapat menyandikan dan mengimplementasikan pemindaian, memproses anotasi, dan menghasilkan file Java dalam metode ini. Menggunakan parameter Roundenvironment, Anda dapat meminta elemen yang dijelaskan dengan anotasi tertentu (Teks Asli: Anda dapat meminta elemen yang dianotasi dengan anotasi tertentu). Kami akan melihat detailnya nanti.
getSupportedAnnotationTypes(): Dalam metode ini Anda harus menentukan anotasi mana yang harus didaftarkan oleh prosesor anotasi. Perhatikan bahwa nilai pengembaliannya adalah koleksi string yang berisi nama lengkap jenis anotasi yang ingin diproses oleh prosesor anotasi Anda. Dengan kata lain, Anda mendefinisikan anotasi mana yang akan ditangani oleh prosesor anotasi Anda.
getSupportedSourceVersion() : Digunakan untuk menentukan versi Java yang Anda gunakan. Biasanya Anda harus mengembalikan SourceVersion.latestSupported() . Namun, jika Anda memiliki cukup alasan untuk tetap berpegang pada Java 6, Anda juga dapat mengembalikan SourceVersion.RELEASE_6 . Saya merekomendasikan menggunakan SourceVersion.latestSupported() . Di Java 7, Anda juga dapat menggunakan anotasi untuk menggantikan penulisan ulang getSupportedAnnotationTypes() dan getSupportedSourceVersion() , seperti yang ditunjukkan di bawah ini:
@SupportedSourCeVersion (value = Sourceversion.Release_7) @supportedannotationTypes ({// Set dari nama anotasi qulified lengkap "com.example.myannotation", "com.example.anotherannotation"}) Public MyProcessor Extends Extends AbstractRprocessor {eoverRide {eoverRide {eoverRide {eoverRide {eoverRide {eoverride "} public myprocessor (EXTENDSPROCESOR @OLOCENSEOR {EVENOCENSE {EVEROCENSE {EVEROCHENSE"} Public MyProcessor Extends Extrend {eoverReArdor {eoverRide {eoverReArtoration " Roundenvironment env) {return false; } @Override public disinkronkan void init (ProcessingEnvironment ProcessingEnv) {super.init (ProcessingEnv); }} Karena masalah kompatibilitas, terutama untuk Android, saya merekomendasikan untuk menulis ulang getSupportedAnnotationTypes() dan getSupportedSourceVersion() alih -alih menggunakan @SupportedAnnotationTypes dan @SupportedSourceVersion .
Hal berikutnya yang harus Anda ketahui adalah: prosesor anotasi berjalan dalam JVM sendiri. Ya, Anda membacanya dengan benar. Javac memulai mesin virtual Java lengkap untuk menjalankan prosesor anotasi. maksudnya itu apa? Anda dapat menggunakan apa pun yang Anda gunakan dalam program Java normal. Menggunakan jambu jala! Anda dapat menggunakan alat injeksi ketergantungan seperti belati atau perpustakaan kelas lainnya yang ingin Anda gunakan. Tapi jangan lupa bahwa meskipun itu hanya prosesor kecil, Anda harus memperhatikan menggunakan algoritma dan pola desain yang efisien, seperti yang Anda lakukan dalam mengembangkan program Java lainnya.
3. Daftarkan prosesor Anda
Anda mungkin bertanya "Bagaimana cara mendaftarkan prosesor anotasi saya ke Javac?". Anda harus memberikan file .jar. Sama seperti file .jar lainnya, Anda mengemas prosesor anotasi yang sudah dikompilasi ke dalam file ini. Dan, dalam file .jar Anda, Anda harus mengemas file khusus javax.annotation.processing.processor ke direktori meta-inf/services. Jadi struktur direktori file .jar Anda terlihat seperti ini:
Myprocess.jar -com -example -myprocess.class -meta -inf -services -javax.annotation.processing.processor
Isi file javax.annotation.processing.processor adalah daftar, dan setiap baris adalah nama lengkap dari prosesor anotasi. Misalnya:
com.example.myprocess
com.example.anotherprocess
4. Contoh: Model Pabrik
Masalah yang ingin kami selesaikan adalah: kami ingin mengimplementasikan toko pizza, yang memberi pelanggan dua pizza (margherita dan calzone), serta hidangan penutup tiramisu (tiramisu).
Public Interface Meal {public float getPrice ();} kelas publik margheritapizza mengimplementasikan makanan {@Override public float getPrice () {return 6.0f; }} kelas publik CalzonePizza mengimplementasikan makanan {@Override public float getPrice () {return 8.5f; }} kelas publik Tiramisu mengimplementasikan makanan {@override float public getPrice () {return 4.5f; }} Public Class Pizzastore {Public Meal Order (String MealName) {if (null == MealName) {Throw new IllegalArgumentException ("Name of Meal is Null!"); } if ("margherita" .Equals (fealname)) {return new margheritapizza (); } if ("calzone" .Equals (MealName)) {return new calzonePizza (); } if ("Tiramisu" .Equals (MealName)) {return new Tiramisu (); } melempar IllegalArgumentException baru ("Meal Tidak Diketahui '" + MealName + "'"); } private static string readConsole () {scanner scanner = pemindai baru (System.in); String Meal = Scanner.Nextline (); scanner.close (); kembali makan; } public static void main (string [] args) {System.out.println ("Selamat Datang di Pizza Store"); Pizzastore Pizzastore = Pizzastore baru (); Makanan Meal = Pizzastore.order (ReadConsole ()); System.out.println ("Bill: $" + Meal.getPrice ()); }}Seperti yang Anda lihat, dalam metode pesanan (), kami memiliki banyak pernyataan penilaian kondisi. Dan, jika kita menambahkan pizza baru, kita harus menambahkan penilaian bersyarat baru. Tapi tunggu, menggunakan prosesor anotasi dan mode pabrik, kita dapat memiliki prosesor anotasi menghasilkan pernyataan jika ini. Dengan cara ini, kode yang kita inginkan terlihat seperti ini:
pizzastore kelas publik {private factory factory = new factory (); Pesanan Makan Umum (String MealName) {return factory.create (MealName); } private static string readConsole () {scanner scanner = pemindai baru (System.in); String Meal = Scanner.Nextline (); scanner.close (); kembali makan; } public static void main (string [] args) {System.out.println ("Selamat Datang di Pizza Store"); Pizzastore Pizzastore = Pizzastore baru (); Makanan Meal = Pizzastore.order (ReadConsole ()); System.out.println ("Bill: $" + Meal.getPrice ()); }} Public Class MealFactory {Public Meal Create (String ID) {if (id == null) {lempar baru ilegalArgumentException ("id is null!"); } if ("calzone" .equals (id)) {return new calzonePizza (); } if ("Tiramisu" .Equals (id)) {return new Tiramisu (); } if ("margherita" .equals (id)) {return baru margheritazza (); } lempar IllegalArgumentException baru ("tidak diketahui id =" + id); }}5. Anotasi @Factory
Bisakah Anda menebak bahwa kami berencana menggunakan prosesor anotasi untuk menghasilkan kelas Mealfactory. Secara lebih umum, kami ingin memberikan anotasi dan prosesor untuk menghasilkan kelas pabrik.
Mari kita lihat anotasi @Factory:
@Target (elementType.type) @retention (retentionpolicy.class) public @interface factory { / ** * Nama pabrik * / class <?> Type (); / ** * Pengidentifikasi untuk menentukan item mana yang harus dipakai */ string id ();}Idenya adalah sebagai berikut: Kami memberi anotasi kelas makanan tersebut, menggunakan tipe () untuk menunjukkan pabrik mana kelas ini milik, dan menggunakan id () untuk menunjukkan jenis spesifik dari kelas ini. Mari terapkan anotasi @Factory ke kelas -kelas ini:
@Factory (type = margheritapizza.class, id = "margherita") kelas publik margheritapizza mengimplementasikan makanan {@override float public getPrice () {return 6.0f; }} @Factory (type = calzonePizza.class, id = "calzone") kelas publik calzonePizza mengimplementasikan makanan {@Override public float getPrice () {return 8.5f; }} @Factory (type = tiramisu.class, id = "tiramisu") kelas publik Tiramisu mengimplementasikan makanan {@Override public float getPrice () {return 4.5f; }}Anda mungkin bertanya, bisakah kita menerapkan anotasi @Factory ke antarmuka makan? Jawabannya adalah tidak, karena anotasi tidak dapat diwarisi. Yaitu, jika ada anotasi pada kelas X, kelas Y meluas X, maka kelas Y tidak akan mewarisi anotasi pada kelas X. Sebelum kita menulis prosesor, kita perlu mengklarifikasi beberapa aturan:
Prosesor Anotasi:
Public Class FactoryProcessor memperluas AbstractProcessor {Typeutils tipe pribadi; Elemen Pribadi Elementutils; filer filer pribadi; Private Messager Messager; peta pribadi <string, factorygroupedclasses> factoryclasses = new LinkedHashMap <string, factorygroupedclasses> (); @Override public disinkronkan void init (ProcessingEnvironment ProcessingEnv) {super.init (ProcessingEnv); typeUtils = ProcessingEnv.getTypeUtils (); elementutils = processingenv.getElementUtils (); filer = processingenv.getFiler (); Messager = ProcessingEnv.GetMessager (); } @Override Public Boolean Process (set <? Extends Typeelement> arg0, Roundenvironment arg1) {... return false; } @Override Public Set <String> GetSupportedAnnotationTypes () {set <string> annotatasions = new LinkedHashSet <String> (); annotataions.add (factory.class.getCanonicalName ()); mengembalikan annotataon; } @Override Public Sourceversion GetSupportedSourCeversion () {return Sourceversion.latestSupported (); }} Dalam metode getSupportedAnnotationTypes() , kami menentukan bahwa anotasi @Factory akan diproses oleh prosesor ini.
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.