1. بعض المفاهيم الأساسية
قبل أن نبدأ ، نحتاج إلى إعلان شيء مهم: نحن لا نناقش التعليقات التوضيحية التي تتم معالجتها من خلال آليات التفكير في وقت التشغيل ، ولكن نناقش التعليقات التوضيحية التي تتم معالجتها في وقت الترجمة.
ما هو الفرق بين التعليقات التوضيحية لوقت التجميع والتعليقات التوضيحية لوقت التشغيل؟ في الواقع ، إنه ليس كبيرًا ، ويرجع ذلك أساسًا إلى مشكلات الأداء. تعتمد شرح وقت التشغيل بشكل أساسي تمامًا على الانعكاس ، وكفاءة الانعكاس أبطأ من تلك الموجودة في تلك الأصلية ، لذلك سيكون هناك بعض الآلات ذات الذاكرة الأقل ومساحات وحدة المعالجة المركزية السيئة. ومع ذلك ، لن يواجه التعليق التوضيحي هذه المشكلة على الإطلاق ، لأنه في عملية التجميع الخاصة بنا (Java-> ، يتم استخدام بعض علامات التعليقات التوضيحية لإنشاء بعض الفئات أو الملفات ديناميكيًا ، لذلك لا علاقة لها بتشغيل APK الخاص بنا ، لذلك لا توجد مشكلة في الأداء بشكل طبيعي. لذلك ، إذا كان مشروع مفتوح المصدر أكثر شهرة يستخدم وظيفة التعليق التوضيحي ، فإنه عادة ما يستخدم التعليق التوضيحي لوقت التجميع.
معالج التعليقات التوضيحية هو أداة تأتي مع Javac ، والتي يتم استخدامها لمسح ومعالجة معلومات التعليقات التوضيحية خلال فترة التجميع. يمكنك تسجيل معالج التعليقات التوضيحية الخاصة بك لبعض التعليقات التوضيحية. هنا ، أفترض أنك تفهم بالفعل ماهية التعليقات التوضيحية وكيفية تخصيصها. إذا لم تكن قد فهمت التعليقات التوضيحية بعد ، فيمكنك التحقق من الوثائق الرسمية. كانت معالجات التعليقات التوضيحية موجودة بالفعل في Java 5 ، ولكن لم يكن هناك واجهة برمجة تطبيقات متاحة حتى Java 6 (تم إصدارها في ديسمبر 2006). استغرق الأمر بعض الوقت قبل أن يدرك مستخدمو Java قوة معالج التعليقات التوضيحية. لذلك أصبحت شعبية فقط في السنوات الأخيرة.
يأخذ المعالج الذي يحتوي على تعليق توضيحي معين رمز مصدر Java (أو ترجمة Bytecode) كإدخال ثم يقوم بإنشاء بعض الملفات (عادة ملفات .java) كإخراج. ماذا يعني ذلك؟ يمكنك إنشاء رمز Java! هذه رموز Java موجودة في ملف .java. لذلك لا يمكنك تغيير فئة Java الحالية ، مثل إضافة طريقة. سيتم تجميع ملفات Java التي تم إنشاؤها بواسطة Javac مثل رمز مصدر Java المكتوب يدويًا.
يتم تنفيذ معالجة التعليقات التوضيحية في مرحلة التجميع. مبدأها هو قراءة رمز مصدر Java ، وتحليل التعليقات التوضيحية ، ثم إنشاء رمز Java الجديد. يتم تجميع رمز Java الذي تم إنشاؤه حديثًا في Java Bytecode. لا يمكن لمعالج التعليقات التوضيحية تغيير فئة القراءة Java ، مثل إضافة أو حذف أساليب Java.
2. الملخص
دعونا نلقي نظرة على واجهة برمجة تطبيقات المعالج. جميع المعالجات ترث مجردة ، كما هو موضح أدناه:
package com.example ؛ import java.util.linkedhashset ؛ import java.util.set ؛ import javax.annotation.processing.abstractProcess javax.annotation.processing.supportedannotationtytypes ؛ import javax.annotation.processing.supportedSourceVersion ؛ import javax.lang.model.sourcevers TypeElement> إعلانات ، RoundenVironment env) {return false ؛ } Override Public Set <string> getSupportEdAntAntationTypes () {set <string> inchataions = new LinkedHashset <String> () ؛ inchataions.add ("com.example.myannotation") ؛ إرجاع ستيرات ؛ } Override public sourceversion getSupportedSourceVersion () {return sourceVersion.LatestSupported () ؛ } Override Public Synchronized void init (ProcessingEnvironment processingenv) {super.init (processingenv) ؛ }}init (ProcessingEnvironment ProcessingEnv): يجب أن تحتوي جميع فئات معالجات التعليقات التوضيحية على مُنشئ بدون معلمة. ومع ذلك ، هناك طريقة خاصة init () ، والتي تسمى أداة معالجة التعليقات التوضيحية ، مع مراعاة البيئة المعالجة كمعلمة. يوفر ProcessingEnvironment بعض عناصر فئات الأدوات العملية وأنواعها والموزعين. سوف نستخدمها لاحقًا.
process(Set<? extends TypeElement> annoations, RoundEnvironment env) : هذا يشبه الطريقة الرئيسية () لكل معالج. يمكنك تشفير وتنفيذ المسح الضوئي ، ومعالجة التعليقات التوضيحية ، وإنشاء ملفات Java في هذه الطريقة. باستخدام معلمة البيئة المستديرة ، يمكنك الاستعلام عن عناصر مشروح بتعليق معين (النص الأصلي: يمكنك الاستعلام عن العناصر المشروحة بتعليق معين). سنرى التفاصيل لاحقًا.
getSupportedAnnotationTypes(): في هذه الطريقة ، يجب عليك تحديد التعليقات التوضيحية التي يجب تسجيلها بواسطة معالج التعليقات التوضيحية. لاحظ أن قيمة الإرجاع الخاصة بها عبارة عن مجموعة سلسلة تحتوي على الاسم الكامل لنوع التعليقات التوضيحية التي يريد معالج التعليقات التوضيحية معالجتها. بمعنى آخر ، أنت تحدد هنا أي معالج التعليقات التوضيحية الخاصة بك سوف يتعامل معها.
getSupportedSourceVersion() : يستخدم لتحديد إصدار Java الذي تستخدمه. عادة ما يجب عليك إرجاع SourceVersion.latestSupported() . ومع ذلك ، إذا كان لديك سبب كاف للالتزام بـ Java 6 ، فيمكنك أيضًا إرجاع SourceVersion.RELEASE_6 . أوصي باستخدام SourceVersion.latestSupported() . في Java 7 ، يمكنك أيضًا استخدام التعليقات التوضيحية لاستبدال إعادة كتابة getSupportedAnnotationTypes() و getSupportedSourceVersion() ، كما هو موضح أدناه:
supportedSourceVersion (value = sourceversion.release_7) supportedAntAntationTytypes ({// مجموعة من أسماء نوع الشرف الكاملة QULISED Roundenvironment Env) {return false ؛ } Override Public Synchronized void init (ProcessingEnvironment processingenv) {super.init (processingenv) ؛ }} نظرًا لقضايا التوافق ، خاصة بالنسبة لنظام Android ، أوصي بإعادة كتابة getSupportedAnnotationTypes() و getSupportedSourceVersion() بدلاً من استخدام @SupportedAnnotationTypes و @SupportedSourceVersion .
الشيء التالي الذي يجب أن تعرفه هو: يعمل معالج التعليقات التوضيحية في JVM الخاصة به. نعم ، لقد قرأت هذا الحق. تبدأ Javac جهاز Java Virtual كامل لتشغيل معالج التعليقات التوضيحية. ماذا يعني ذلك؟ يمكنك استخدام أي شيء تستخدمه في برنامج Java العادي. باستخدام الجوافة! يمكنك استخدام أدوات حقن التبعية مثل Dagger أو أي مكتبة فئة أخرى تريد استخدامها. ولكن لا تنسى أنه حتى لو كان مجرد معالج صغير ، فيجب عليك الانتباه إلى استخدام خوارزميات وتصميم فعالة ، تمامًا كما تفعل في تطوير برامج Java الأخرى.
3. سجل المعالج الخاص بك
قد تسأل "كيفية تسجيل معالج التعليقات التوضيحية الخاصة بي على Javac؟". يجب عليك توفير ملف .jar. تمامًا مثل ملفات .jar الأخرى ، يمكنك حزم معالج التعليقات التوضيحية التي تم تجميعها بالفعل في هذا الملف. وفي ملف .jar الخاص بك ، يجب عليك حزمة ملف خاص javax.annotation.processing.processor في دليل meta-inf/services. لذا فإن هيكل دليل ملف .jar الخاص بك يشبه هذا:
myprocess.jar -com -example -Myprocess.class -Meta -Inf -services -javax.annotation.processing.processor
محتويات ملف javax.annotation.processing.processor هي قائمة ، وكل سطر هو الاسم الكامل لمعالج التعليقات التوضيحية. على سبيل المثال:
com.example.myprocess
com.example.anotherprocess
4. مثال: نموذج المصنع
المشكلة التي نريد حلها هي: نريد تنفيذ متجر بيتزا ، والذي يوفر للعملاء بيتزا (Margherita و Calzone) ، وكذلك الحلوى Tiramisu (Tiramisu).
وجبة الواجهة العامة {public float getPrice () ؛} الطبقة العامة margheritapizza تنفذ الوجبة {Override public float getPrice () {return 6.0f ؛ }} الفئة العامة CalzonePizza تنفذ الوجبة {Override Public Float getPrice () {return 8.5f ؛ }} الفئة العامة tiramisu تنفذ الوجبة {Override public float getPrice () {return 4.5f ؛ }} الفئة العامة Pizzastore {Order Public Meal Order (string geathame) {if (null == geakname) {throw new alficalArgumentException ("اسم الوجبة خالية!") ؛ } if ("margherita" .equals (geathame)) {return new margheritapizza () ؛ } if ("calzone" .equals (geathame)) {return calzonePizza () ؛ } if ("tiramisu" .equals (geathame)) {return new tiramisu () ؛ } رمي غير alfictalargumentException ("غير معروف وجبة" + اسم " +" "") ؛ ) سلسلة الوجبة = الماسحة الضوئية .nextLine () ؛ Scanner.Close () ؛ عودة الوجبة } public static void main (string [] args) {system.out.println ("مرحبًا بك في متجر البيتزا") ؛ Pizzastore Pizzastore = New Pizzastore () ؛ وجبة وجبة = pizzastore.order (readconsole ()) ؛ System.out.println ("Bill: $" + geat.getPrice ()) ؛ }}كما ترون ، في طريقة الترتيب () ، لدينا العديد من بيانات حكم الشرط. وإذا أضفنا بيتزا جديدة ، فيجب علينا إضافة حكم جديد إذا كان مشروطًا. لكن انتظر ، باستخدام معالج التعليقات التوضيحية ووضع المصنع ، يمكننا أن يكون لدينا معالج شرح توليد هذه البيانات. وبهذه الطريقة ، يبدو الرمز الذي نريده هكذا:
PIZZASTORE من الطبقة العامة Order Public Meal Order (string geaking) {return factory.create (geathame) ؛ ) سلسلة الوجبة = الماسحة الضوئية .nextLine () ؛ Scanner.Close () ؛ عودة الوجبة } public static void main (string [] args) {system.out.println ("مرحبًا بك في متجر البيتزا") ؛ Pizzastore Pizzastore = New Pizzastore () ؛ وجبة وجبة = pizzastore.order (readconsole ()) ؛ System.out.println ("Bill: $" + geat.getPrice ()) ؛ }} الفئة العامة للوجبات {الوجبة العامة إنشاء (معرف السلسلة) {if (id == null) {رمي new alficalArgumentException ("id is null!") ؛ } if ("calzone" .equals (id)) {return new CalzonePizza () ؛ } if ("tiramisu" .equals (id)) {return new tiramisu () ؛ } if ("margherita" .equals (id)) {return new Margheritapizza () ؛ } رمي غير alfictalargumentException جديد ("غير معروف معرف =" + معرف) ؛ }}5. @التعليق التوضيحي
هل يمكنك تخمين أننا نخطط لاستخدام معالج التعليقات التوضيحية لإنشاء فئة الوجبة. بشكل أعم ، نريد تقديم تعليق توضيحي ومعالج لإنشاء فصول المصنع.
دعونا نلقي نظرة على التعليق التوضيحي factory:
target (elementType.type) retention (attreencepolicy.class) public interface factory { / ** * اسم المصنع * / class <؟> type () ؛ / ** * المعرف لتحديد العنصر الذي يجب إنشاء مثيله */ string id () ؛}الفكرة هي كما يلي: نقوم بتعليق فصول الطعام هذه ، ونستخدم النوع () للإشارة إلى المصنع الذي ينتمي إليه هذا الفئة ، ونستخدم المعرف () للإشارة إلى النوع المحدد من هذه الفئة. دعنا نطبق التعليق التوضيحي factory على هذه الفئات:
factory (type = margheritapizza.class ، id = "margherita") الطبقة العامة margheritapizza تنفذ الوجبة {Override public float getPrice () {return 6.0f ؛ }} @factory (type = calzonepizza.class ، id = "calzone") class calzonepizza العامة تنفذ الوجبة {override public float getPrice () {return 8.5f ؛ }} @factory (type = tiramisu.class ، id = "tiramisu") public class tiramisu تنفذ الوجبة {override public float getPrice () {return 4.5f ؛ }}قد تسأل ، هل يمكننا تطبيق التعليق التوضيحي factory على واجهة الوجبة؟ الجواب هو لا ، لأنه لا يمكن أن يتم مورث التعليقات التوضيحية. أي إذا كان هناك تعليقات توضيحية على الفئة X ، فسيتم فئة Y X ، فلن يرث الفئة Y التعليقات التوضيحية على الفئة X. قبل أن نكتب معالجًا ، نحتاج إلى توضيح بعض القواعد:
معالج التعليقات التوضيحية:
يمتد FactoryProcessor من الطبقة العامة ExclostProcessor {typeutils الخاصة ؛ العناصر الخاصة elementUtils. Filer Filer الخاص ؛ Messager Messager الخاص ؛ خريطة خاصة <string ، factorygroupedClasses> factoryclasses = new LinkedHashMap <string ، factorygroupedClasses> () ؛ Override Public Synchronised 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> getSupportEdAntAntationTypes () {set <string> inchataions = new LinkedHashset <String> () ؛ inctataions.add (factory.class.getCanonicalName ()) ؛ إرجاع ستيرات ؛ } Override public sourceversion getSupportedSourceVersion () {return sourceVersion.LatestSupported () ؛ }} في طريقة getSupportedAnnotationTypes() ، نحدد أن التعليقات التوضيحية @Factory ستتم معالجتها بواسطة هذا المعالج.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.