我正面临一个问题,我必须修改包信息.
package-info.java
@javax.xml.bind.annotation.XmlSchema(namespace = "http://some.url/soap/style/document_literal") package org.example.wsdl.wsdl;
以下代码适用于1.7.0_45.
// do not load any classes before,this could break the following code. Class<?> pkgInfo = Class.forName("org.example.wsdl.package-info",true,NameSpaceModifier.class.getClassLoader()); Field field = Class.class.getDeclaredField("annotations"); field.setAccessible(true); final XmlSchema oldAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0]; logger.debug("Old Annotation namespace value was: " + oldAnnotation.namespace()); XmlSchema newAnnotation = new XmlSchema() { @Override public XmlNs[] xmlns() { return oldAnnotation.xmlns(); } @Override public String namespace() { return "newNs"; } @Override public XmlNsForm elementFormDefault() { return oldAnnotation.elementFormDefault(); } @Override public XmlNsForm attributeFormDefault() { return oldAnnotation.attributeFormDefault(); } @Override public String location() { return oldAnnotation.location(); } @Override public Class<? extends Annotation> annotationType() { return oldAnnotation.annotationType(); } }; @SuppressWarnings("unchecked") Map<Class<? extends Annotation>,Annotation> annotations = (Map<Class<? extends Annotation>,Annotation>) field.get(pkgInfo); annotations.put(XmlSchema.class,newAnnotation); XmlSchema modifiedAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0];
使用1.8.0_05编译和执行相同的代码时,我收到以下错误消息:
java.lang.NoSuchFieldException: annotations at java.lang.Class.getDeclaredField(Class.java:2057)
我知道它是一个哈克,至少它看起来像一个.但Java 8是否按预期工作?
我如何更改它与Java 8一起使用的代码呢?
Javassist的答案也很受欢迎;)
解决方法
Java 8改变了内部存储注释的方式.由于您使用的是具有硬编码字段名称的恶意反射黑客,因此任何Java更新都有可能重新破坏您的代码.
java.lang.Class中:
/** * @since 1.5 */ public Annotation[] getAnnotations() { return AnnotationParser.toArray(annotationData().annotations); } private volatile transient AnnotationData annotationData; private AnnotationData annotationData() { while (true) { // retry loop AnnotationData annotationData = this.annotationData; int classRedefinedCount = this.classRedefinedCount; if (annotationData != null && annotationData.redefinedCount == classRedefinedCount) { return annotationData; } // null or stale annotationData -> optimistically create new instance AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount); // try to install it if (Atomic.casAnnotationData(this,annotationData,newAnnotationData)) { // successfully installed new AnnotationData return newAnnotationData; } } } private static class AnnotationData { final Map<Class<? extends Annotation>,Annotation> annotations; final Map<Class<? extends Annotation>,Annotation> declaredAnnotations; // Value of classRedefinedCount when we created this AnnotationData instance final int redefinedCount; AnnotationData(Map<Class<? extends Annotation>,Annotation> annotations,Map<Class<? extends Annotation>,Annotation> declaredAnnotations,int redefinedCount) { this.annotations = annotations; this.declaredAnnotations = declaredAnnotations; this.redefinedCount = redefinedCount; } }
我想你可以使用新的字段值来临时修复你的代码.永久修复是更改注释本身,而不是在运行时动态修改它.
Field annotationDataField = Class.class.getDeclaredField("annotationData"); annotationDataField.setAccessible(true); Object annotationData = annotationDataField.get(pkgInfo); Field annotationsField = annotationData.getClass().getDeclaredField("annotations"); annotationsField.setAccessible(true); Map<Class<? extends Annotation>,Annotation>) annotationsField .get(annotationData); annotations.put(XmlSchema.class,newAnnotation); XmlSchema modifiedAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0];