문제

런타임에 객체 (특히 메소드)에 주석을 추가 할 수 있습니까?

조금 더 설명하기 위해 : 모듈과 모듈의 두 가지 모듈이 있습니다. 모듈은 모듈레에 의존하며, 아무것도 의존하지 않습니다. (MODA는 내 핵심 데이터 유형 및 인터페이스이며, MODB는 DB/데이터 계층입니다) MODB는 외부 알리브리에 의존합니다. 제 경우에는 MODB가 Moda에서 Externallibrary로 수업을 나눠주고 있으며 특정 방법을 주석을 달아야합니다. 특정 주석은 모두 Externallib의 일부이며, 내가 말했듯이 Moda는 Externallib에 의존하지 않으며 그렇게 유지하고 싶습니다.

그렇다면 이것이 가능합니까, 아니면이 문제를 보는 다른 방법에 대한 제안이 있습니까?

도움이 되었습니까?

해결책

런타임에 주석을 추가 할 수는 없습니다. 어댑터 해당 모듈 B는 모듈에서 객체를 랩핑하는 데 필요한 주석이 달린 메소드를 노출시키는 데 사용됩니다.

다른 팁

바이트 코드 계측 라이브러리를 통해 가능합니다 Javassist.

특히, 살펴보십시오 주석 변호사 주석을 작성 / 설정하는 방법에 대한 예제 및 바이트 코드 API의 튜토리얼 섹션 클래스 파일을 조작하는 방법에 대한 일반적인 지침.

이것은 간단하고 간단한 것입니다. 저는이 접근법을 추천하지 않으며 수많은 수업을 위해이 작업을 수행 할 필요가 없다면 Tom의 답변을 고려할 것을 제안합니다 (또는 런타임까지 클래스를 사용할 수 없어서 작성하여 작성할 수 없습니다. 어댑터는 불가능합니다).

Java Reflection API를 사용하여 런타임에 Java 클래스에 주석을 추가 할 수도 있습니다. 본질적으로 클래스에 정의 된 내부 주석지도를 재현해야합니다. java.lang.Class (또는 내부 클래스에 정의 된 Java 8의 경우 java.lang.Class.AnnotationData). 당연히이 접근법은 상당히 해킹되며 새로운 Java 버전의 경우 언제든지 깨질 수 있습니다. 그러나 빠르고 더러운 테스트/프로토 타이핑을 위해이 접근법은 때때로 유용 할 수 있습니다.

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 객체에 추가 할 수 있습니다 (그러나 반사를 통해 기존 유형을 혼란스럽게하는 것은 위험하고 디버깅하기가 어려울 수 있기 때문에 대체 방법을 찾는 것이 좋습니다).

하지만 쉽지 않습니다 ... 나는 라이브러리를 썼습니다. Javanna 깨끗한 API를 사용하여 쉽게 할 수 있습니다.

들어 왔어요 JCENTER 그리고 Maven Central.

사용 :

@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 );

이것은 미니 프레임 워크를 만드는 데 편리합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top