使用annotationForMap实例化注解

sun.reflect.annotation.AnnotationParser#annotationForMap

    /**
     * Returns an annotation of the given type backed by the given
     * member -> value map.
     */
    public static Annotation annotationForMap(final Class<? extends Annotation> type,
                                              final Map<String, Object> memberValues)
    {
        return AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
            public Annotation run() {
                return (Annotation) Proxy.newProxyInstance(
                    type.getClassLoader(), new Class<?>[] { type },
                    new AnnotationInvocationHandler(type, memberValues));
            }});
    }

此方法可以根据typememberValues两个入参生成一个对应的注解实例,可以近似理解成使用反射技术生成一个类的实例。可以看到,他的返回值是一个很熟悉的方法,没错,就是JDK动态代理中用于实现切面逻辑的方法java.lang.reflect.Proxy#newProxyInstance。那么就好办了,我们只需要看懂new AnnotationInvocationHandler(type, memberValues)这个类就行了。下面是这个类的主要代码:

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
    
	AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();

        // Handle Object and Annotation methods
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an annotation method");

        switch(member) {
        case "toString":
            return toStringImpl();
        case "hashCode":
            return hashCodeImpl();
        case "annotationType":
            return type;
        }

        // Handle annotation member accessors
        Object result = memberValues.get(member);

        if (result == null)
            throw new IncompleteAnnotationException(type, member);

        if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();

        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);

        return result;
    }
    
    ...
}

invoke方法中,member是方法名,memberValues是传入的键值对,意思就是,只要传入注解的类,和类中方法名对应的值,就可以生成一个注解

编写如下代码进行测试:

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    public @interface AnnoProxyTest
    {
        String value();
    }    


	private void testAnnotationForMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("value", "hello");
        Annotation annotation = AnnotationParser.annotationForMap(AnnoProxyTest.class, map);
        System.err.println(annotation);
        System.err.println(annotation instanceof AnnoProxyTest);
        AnnoProxyTest annoProxyTest = (AnnoProxyTest) annotation;
        System.err.println(annoProxyTest.value());
    }

// 输出:
// > @com.xml.demo.basicknowledge.proxy.anno.AnnoProxy$AnnoProxyTest(value=hello)
// > true
// > hello

看结果就是生成了一个正常的注解。

再搞点骚操作,写一个半成的

    private void testCreateAnnotation() {
        Class<? extends Annotation> type = AnnoProxyTest.class;

        Map<String, Object> memberValues = new HashMap<>();
        memberValues.put("value", "1");

        Annotation annotation = (Annotation) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] {type},
                (proxy, method, args) -> memberValues.get(method.getName()));

        System.err.println(annotation);
        System.err.println(annotation instanceof AnnoProxyTest);
        AnnoProxyTest annoProxyTest = (AnnoProxyTest) annotation;
        System.err.println(annoProxyTest.value());
    }

// 输出:
// > null
// > true
// > 1

也是可以正常输出的,只不过没有实现toString方法,所以输出为null,但是调用其中方法还是可以正常输出的,且类型也是正确的

总结:注解就是一个JDK代理类

AccessController.doPrivileged

AccessController.doPrivileged - 山河已无恙 - 博客园 (cnblogs.com)

posted @ 2024-02-02 15:35  PaomoHH  阅读(54)  评论(0)    收藏  举报