如何在Java注解类中添加自定义静态变量?(仿Log4j2实现)
实现自定义注解处理器为类添加静态变量
核心限制说明
Java标准注解处理器(继承AbstractProcessor的实现)无法修改已存在的源文件或类文件,只能生成新的源文件。如果要直接在标注@TestAnnotation的原类中添加静态变量并直接调用,必须结合字节码增强库(如ASM);如果接受生成新类后通过静态导入/继承使用,可以用纯注解处理器方案。
方案1:纯注解处理器(生成辅助类,静态导入使用)
1. 定义@TestAnnotation注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface TestAnnotation { // 可配置静态变量的默认值 String value() default "Hello Generated"; }
2. 实现注解处理器TestProcessor
用JavaPoet简化代码生成(比原生API更直观),先添加Maven依赖:
<dependency> <groupId>com.squareup</groupId> <artifactId>javapoet</artifactId> <version>1.13.0</version> <scope>provided</scope> </dependency>
处理器代码:
import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; import com.google.auto.service.AutoService; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import java.io.IOException; import java.util.Set; @AutoService(javax.annotation.processing.Processor.class) @SupportedAnnotationTypes("com.yourpackage.TestAnnotation") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class TestProcessor extends AbstractProcessor { private Filer filer; private Messager messager; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); filer = processingEnv.getFiler(); messager = processingEnv.getMessager(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(TestAnnotation.class)) { if (!(element instanceof TypeElement)) { messager.printMessage(Diagnostic.Kind.ERROR, "@TestAnnotation只能标注在类上"); continue; } TypeElement typeElement = (TypeElement) element; String className = typeElement.getSimpleName().toString(); String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).getQualifiedName().toString(); TestAnnotation annotation = typeElement.getAnnotation(TestAnnotation.class); String staticValue = annotation.value(); // 生成public static final类型的静态变量 FieldSpec testStringField = FieldSpec.builder(String.class, "testString", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", staticValue) .build(); // 生成与原类同包的辅助类,命名为原类名+Constants TypeSpec helperClass = TypeSpec.classBuilder(className + "Constants") .addModifiers(Modifier.PUBLIC) .addField(testStringField) .build(); try { JavaFile.builder(packageName, helperClass) .build() .writeTo(filer); } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "生成辅助类失败:" + e.getMessage()); } } return true; } }
3. 使用方式
在原类中静态导入辅助类的变量即可直接调用:
@TestAnnotation("我的自定义静态值") public class DemoClass { import static com.yourpackage.DemoClassConstants.testString; public static void main(String[] args) { System.out.println(testString); // 输出:我的自定义静态值 } }
方案2:注解处理器+ASM字节码增强(直接修改原类)
如果要像Log4j2那样直接在原类中生成静态变量,需要用ASM修改编译后的字节码:
1. 添加ASM依赖
<dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>9.5</version> <scope>provided</scope> </dependency>
2. 修改处理器实现
import com.google.auto.service.AutoService; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Opcodes; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Set; @AutoService(javax.annotation.processing.Processor.class) @SupportedAnnotationTypes("com.yourpackage.TestAnnotation") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class TestProcessor extends AbstractProcessor { private Filer filer; private Messager messager; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); filer = processingEnv.getFiler(); messager = processingEnv.getMessager(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(TestAnnotation.class)) { if (!(element instanceof TypeElement)) { messager.printMessage(Diagnostic.Kind.ERROR, "@TestAnnotation只能标注在类上"); continue; } TypeElement typeElement = (TypeElement) element; String fullClassName = typeElement.getQualifiedName().toString(); String internalClassName = fullClassName.replace('.', '/'); TestAnnotation annotation = typeElement.getAnnotation(TestAnnotation.class); String staticValue = annotation.value(); try { // 读取原类的字节码 JavaFileObject classFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", internalClassName + ".class"); InputStream inputStream = classFile.openInputStream(); ClassReader classReader = new ClassReader(inputStream); // 用ClassWriter修改字节码 ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9, classWriter) { @Override public void visitEnd() { // 在类末尾添加public static final String testString字段 FieldVisitor fieldVisitor = visitField( Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "testString", "Ljava/lang/String;", null, staticValue ); if (fieldVisitor != null) { fieldVisitor.visitEnd(); } super.visitEnd(); } }; classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES); inputStream.close(); // 写入修改后的字节码 OutputStream outputStream = filer.createClassFile(fullClassName).openOutputStream(); outputStream.write(classWriter.toByteArray()); outputStream.close(); } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "修改类字节码失败:" + e.getMessage()); } } return true; } }
3. 使用方式
直接在标注注解的类中调用静态变量:
@TestAnnotation("直接在原类调用的静态值") public class DemoClass { public static void main(String[] args) { System.out.println(testString); // 输出:直接在原类调用的静态值 } }
处理器注册说明
如果不用@AutoService注解,需要手动在src/main/resources/META-INF/services/javax.annotation.processing.Processor文件中添加处理器的全类名:
com.yourpackage.TestProcessor
内容的提问来源于stack exchange,提问作者hteppl
相关产品推荐
相关产品推荐

