[英]Unable to modify annotation of package-info.java using Java 8
我遇到一个必须修改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)
我知道它是Hack,至少看起来像一个。 但是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> annotations = (Map<Class<? extends Annotation>, Annotation>) annotationsField
.get(annotationData);
annotations.put(XmlSchema.class, newAnnotation);
XmlSchema modifiedAnnotation = (XmlSchema) pkgInfo.getAnnotations()[0];
我遇到了需要外部化XmlSchema
批注的namespace
属性的情况。 因为它需要一个恒定的值,所以我转而反思,找到了这篇文章。 但这对我来说太狭hack了,并且当操作员也欢迎Javassist的答案时,这是一个:
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("org.example.wsdl.package-info");
ClassFile ccFile = cc.getClassFile();
ConstPool cp = ccFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
Annotation as = new Annotation("javax.xml.bind.annotation.XmlSchema", cp);
as.addMemberValue("namespace", new StringMemberValue("newNs", cp));
EnumMemberValue emv = new EnumMemberValue(cp);
emv.setType("javax.xml.bind.annotation.XmlNsForm");
emv.setValue("QUALIFIED");
as.addMemberValue("elementFormDefault", emv);
// Others member values can be added manually or cloned from the original annotation like in the op's example
attr.addAnnotation(as);
ccFile.addAttribute(attr);
cc.toClass();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.