سؤال

هل من الممكن إضافة تعليق توضيحي إلى كائن (في حالتي على وجه الخصوص ، طريقة) في وقت التشغيل؟

لمزيد من التفسير: لدي وحدتان ، Modulea و ModuleB. يعتمد ModuleB على Modulea ، والذي لا يعتمد على أي شيء. (MODA عبارة عن أنواع البيانات والواجهات الأساسية الخاصة بي ، وهكذا ، يعتمد MODB DB/DATA) أيضًا على مكتبة الأدوية الخارجية. في حالتي ، يقوم MODB بتسليم فصل من MODA إلى مكتبة خارجية ، والتي تحتاج إلى شرح طرق معينة. تعتبر التعليقات التوضيحية المحددة كلها جزءًا من Externallib ، وكما قلت ، لا يعتمد مودا على externallib وأود أن أبقيه على هذا النحو.

إذن ، هل هذا ممكن ، أم لديك اقتراحات لطرق أخرى للنظر في هذه المشكلة؟

هل كانت مفيدة؟

المحلول

لا يمكن إضافة تعليق توضيحي في وقت التشغيل ، يبدو أنك بحاجة إلى تقديم مشترك كهربائي تستخدم هذه الوحدة B لف الكائن من الوحدة النمطية A الكشف عن طرق المشروح المطلوبة.

نصائح أخرى

من الممكن عبر مكتبة أجهزة Bytecode مثل جافاسيست.

على وجه الخصوص ، ألق نظرة على التعليقات التوضيحية الفصل للحصول على مثال على كيفية إنشاء / ضبط التعليقات التوضيحية و قسم البرنامج التعليمي على API bytecode للحصول على إرشادات عامة حول كيفية معالجة ملفات الفصل.

هذا ليس سوى شيء بسيط ومباشر ، على الرغم من ذلك - لا أوصي بهذا النهج وأقترح عليك أن تفكر في إجابة توم بدلاً من ذلك ما لم تكن بحاجة إلى القيام بذلك لعدد كبير من الفصول الدراسية (أو الفصول الدراسية المذكورة غير متوفرة لك حتى وقت التشغيل وبالتالي الكتابة محول مستحيل).

من الممكن أيضًا إضافة تعليق توضيحي إلى فئة Java في وقت التشغيل باستخدام Java Reflection API. يجب على المرء أن يعيد إنشاء خرائط التعليقات التوضيحية الداخلية المحددة في الفصل java.lang.Class (أو لـ Java 8 المحددة في الفصل الداخلي java.lang.Class.AnnotationData). بطبيعة الحال ، هذا النهج هو اختراق تمامًا وقد ينكسر في أي وقت لإصدارات Java الأحدث. ولكن بالنسبة للاختبار السريع/النماذج الأولية ، يمكن أن يكون هذا النهج مفيدًا في بعض الأحيان.

مثال على المفهوم لجافا 8:

public final class RuntimeAnnotations {

    private static final Constructor<?> AnnotationInvocationHandler_constructor;
    private static final Constructor<?> AnnotationData_constructor;
    private static final Method Class_annotationData;
    private static final Field Class_classRedefinedCount;
    private static final Field AnnotationData_annotations;
    private static final Field AnnotationData_declaredAnotations;
    private static final Method Atomic_casAnnotationData;
    private static final Class<?> Atomic_class;

    static{
        // static initialization of necessary reflection Objects
        try {
            Class<?> AnnotationInvocationHandler_class = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            AnnotationInvocationHandler_constructor = AnnotationInvocationHandler_class.getDeclaredConstructor(new Class[]{Class.class, Map.class});
            AnnotationInvocationHandler_constructor.setAccessible(true);

            Atomic_class = Class.forName("java.lang.Class$Atomic");
            Class<?> AnnotationData_class = Class.forName("java.lang.Class$AnnotationData");

            AnnotationData_constructor = AnnotationData_class.getDeclaredConstructor(new Class[]{Map.class, Map.class, int.class});
            AnnotationData_constructor.setAccessible(true);
            Class_annotationData = Class.class.getDeclaredMethod("annotationData");
            Class_annotationData.setAccessible(true);

            Class_classRedefinedCount= Class.class.getDeclaredField("classRedefinedCount");
            Class_classRedefinedCount.setAccessible(true);

            AnnotationData_annotations = AnnotationData_class.getDeclaredField("annotations");
            AnnotationData_annotations.setAccessible(true);
            AnnotationData_declaredAnotations = AnnotationData_class.getDeclaredField("declaredAnnotations");
            AnnotationData_declaredAnotations.setAccessible(true);

            Atomic_casAnnotationData = Atomic_class.getDeclaredMethod("casAnnotationData", Class.class, AnnotationData_class, AnnotationData_class);
            Atomic_casAnnotationData.setAccessible(true);

        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException e) {
            throw new IllegalStateException(e);
        }
    }

    public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, Map<String, Object> valuesMap){
        putAnnotation(c, annotationClass, annotationForMap(annotationClass, valuesMap));
    }

    public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, T annotation){
        try {
            while (true) { // retry loop
                int classRedefinedCount = Class_classRedefinedCount.getInt(c);
                Object /*AnnotationData*/ annotationData = Class_annotationData.invoke(c);
                // null or stale annotationData -> optimistically create new instance
                Object newAnnotationData = createAnnotationData(c, annotationData, annotationClass, annotation, classRedefinedCount);
                // try to install it
                if ((boolean) Atomic_casAnnotationData.invoke(Atomic_class, c, annotationData, newAnnotationData)) {
                    // successfully installed new AnnotationData
                    break;
                }
            }
        } catch(IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e){
            throw new IllegalStateException(e);
        }

    }

    @SuppressWarnings("unchecked")
    private static <T extends Annotation> Object /*AnnotationData*/ createAnnotationData(Class<?> c, Object /*AnnotationData*/ annotationData, Class<T> annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) AnnotationData_annotations.get(annotationData);
        Map<Class<? extends Annotation>, Annotation> declaredAnnotations= (Map<Class<? extends Annotation>, Annotation>) AnnotationData_declaredAnotations.get(annotationData);

        Map<Class<? extends Annotation>, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations);
        newDeclaredAnnotations.put(annotationClass, annotation);
        Map<Class<? extends Annotation>, Annotation> newAnnotations ;
        if (declaredAnnotations == annotations) {
            newAnnotations = newDeclaredAnnotations;
        } else{
            newAnnotations = new LinkedHashMap<>(annotations);
            newAnnotations.put(annotationClass, annotation);
        }
        return AnnotationData_constructor.newInstance(newAnnotations, newDeclaredAnnotations, classRedefinedCount);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Annotation> T annotationForMap(final Class<T> annotationClass, final Map<String, Object> valuesMap){
        return (T)AccessController.doPrivileged(new PrivilegedAction<Annotation>(){
            public Annotation run(){
                InvocationHandler handler;
                try {
                    handler = (InvocationHandler) AnnotationInvocationHandler_constructor.newInstance(annotationClass,new HashMap<>(valuesMap));
                } catch (InstantiationException | IllegalAccessException
                        | IllegalArgumentException | InvocationTargetException e) {
                    throw new IllegalStateException(e);
                }
                return (Annotation)Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, handler);
            }
        });
    }
}

مثال الاستخدام:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestAnnotation {
    String value();
}

public static class TestClass{}

public static void main(String[] args) {
    TestAnnotation annotation = TestClass.class.getAnnotation(TestAnnotation.class);
    System.out.println("TestClass annotation before:" + annotation);

    Map<String, Object> valuesMap = new HashMap<>();
    valuesMap.put("value", "some String");
    RuntimeAnnotations.putAnnotation(TestClass.class, TestAnnotation.class, valuesMap);

    annotation = TestClass.class.getAnnotation(TestAnnotation.class);
    System.out.println("TestClass annotation after:" + annotation);
}

انتاج:

TestClass annotation before:null
TestClass annotation after:@RuntimeAnnotations$TestAnnotation(value=some String)

قيود هذا النهج:

  • قد تكسر الإصدارات الجديدة من Java الرمز في أي وقت.
  • يعمل المثال أعلاه فقط مع Java 8 - مما يجعلها تعمل مع إصدارات Java الأقدم ، سيتطلب التحقق من إصدار Java في وقت التشغيل وتغيير التنفيذ وفقًا لذلك.
  • إذا حصل الفصل المشروح إعادة تعريف (على سبيل المثال أثناء تصحيح الأخطاء) ، سيتم فقد التعليق التوضيحي.
  • لم يتم اختباره بدقة ؛ لست متأكدًا مما إذا كانت هناك أي آثار جانبية سيئة - استخدام على مسؤوليتك الخاصة...

من الممكن إنشاء تعليقات توضيحية في وقت التشغيل عبر أ الوكيل. يمكنك بعد ذلك إضافتها إلى كائنات Java الخاصة بك عبر الانعكاس كما هو مقترح في إجابات أخرى (ولكن من المحتمل أن تكون أفضل حالًا من العثور على طريقة بديلة للتعامل مع ذلك ، لأن العبث مع الأنواع الموجودة عبر الانعكاس يمكن أن يكون خطيرًا وصعب تصحيحه).

لكن الأمر ليس سهلاً للغاية ... كتبت مكتبة تسمى ، آمل بشكل مناسب ، جافانا فقط للقيام بذلك بسهولة باستخدام واجهة برمجة تطبيقات نظيفة.

انها في jcenter و مافن سنترال.

استخدامه:

@Retention( RetentionPolicy.RUNTIME )
@interface Simple {
    String value();
}

Simple simple = Javanna.createAnnotation( Simple.class, 
    new HashMap<String, Object>() {{
        put( "value", "the-simple-one" );
    }} );

إذا لم يتطابق أي إدخال للخريطة مع الحقل (الحقل) المعلن عن التعليقات التوضيحية ، يتم طرح استثناء. إذا كانت أي قيمة ليس لها قيمة افتراضية مفقودة ، يتم طرح استثناء.

هذا يجعل من الممكن افتراض أن كل مثيل شرح يتم إنشاؤه بنجاح هو آمن لاستخدامه مثل مثيل التعليق التوضيحي لوقت التجميع.

كمكافأة ، يمكن أن يحلّف هذا LIB أيضًا فصول التعليقات التوضيحية وإرجاع قيم التعليقات التوضيحية كخريطة:

Map<String, Object> values = Javanna.getAnnotationValues( annotation );

هذا مريح لإنشاء Frameworks المصغرة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top