如何通过注解处理器强制子类重写父类方法时添加指定注解?
嗨,我看了你遇到的问题——想让子类重写父类特定方法时必须加上指定注解,但自己写的注解处理器没生效对吧?我帮你梳理下当前实现里的问题,以及怎么修正~
一、当前注解处理器的核心问题
你的处理器现在完全没触达子类的重写方法,还有类型判断错误,导致根本没执行检查逻辑:
1. 类型判断错误,直接跳过了方法元素
你在处理器里判断element instanceof VariableElement,但@EnforceAnnotation是标注在方法上的,对应的Element类型应该是ExecutableElement(用于表示方法/构造器元素),而VariableElement是用来表示字段、参数这类变量的。所以你的循环里的判断条件永远不成立,压根没进入检查逻辑!
2. 逻辑只盯着父类的注解方法,没扫描子类的重写方法
你的处理器现在只遍历被@EnforceAnnotation标注的元素(也就是父类的那个方法),完全没去查找所有重写了该方法的子类方法,自然没法检查这些子类方法有没有加要求的注解。
二、修正后的注解处理器实现
我们需要调整处理器逻辑:先拿到被@EnforceAnnotation标注的父类方法,再找到所有重写了该方法的子类方法,最后检查这些子类方法是否带有指定注解。
修正后的EnforceAnnotationProcessor.java代码:
@SupportedAnnotationTypes("org.bbloggsbott.annotationenforcer.EnforceAnnotation") @SupportedSourceVersion(SourceVersion.RELEASE_21) public class EnforceAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Starting EnforceAnnotationProcessor"); Messager messager = processingEnv.getMessager(); Types typeUtils = processingEnv.getTypeUtils(); Elements elementUtils = processingEnv.getElementUtils(); for (Element element : roundEnv.getElementsAnnotatedWith(EnforceAnnotation.class)) { // 修正类型判断:方法元素对应ExecutableElement if (!(element instanceof ExecutableElement parentMethod)) { continue; } EnforceAnnotation enforceAnnotation = parentMethod.getAnnotation(EnforceAnnotation.class); Class<?> requiredAnnotationClass = enforceAnnotation.value(); String requiredAnnotationName = requiredAnnotationClass.getCanonicalName(); TypeElement requiredAnnotationType = elementUtils.getTypeElement(requiredAnnotationName); if (requiredAnnotationType == null) { messager.printMessage(Diagnostic.Kind.ERROR, "指定的注解类不存在: " + requiredAnnotationName, parentMethod); continue; } // 1. 拿到父类方法的签名,用于匹配子类的重写方法 ExecutableType parentMethodType = typeUtils.asMemberOf( typeUtils.getDeclaredType((TypeElement) parentMethod.getEnclosingElement()), parentMethod); // 2. 遍历所有可访问的类型,查找重写了该方法的子类方法 for (Element candidateElement : roundEnv.getRootElements()) { if (!(candidateElement instanceof TypeElement subclassType)) { continue; } // 检查当前类是否是父类的子类/实现类 TypeElement parentType = (TypeElement) parentMethod.getEnclosingElement(); if (!typeUtils.isSubtype( elementUtils.getTypeElement(subclassType.getQualifiedName().toString()).asType(), typeUtils.getDeclaredType(parentType))) { continue; } // 遍历子类的所有方法,筛选出重写父类方法的那个 for (Element subclassElement : subclassType.getEnclosedElements()) { if (!(subclassElement instanceof ExecutableElement subclassMethod)) { continue; } // 判断当前子类方法是否重写了目标父类方法 if (typeUtils.isSubsignature(subclassMethod.asType(), parentMethodType)) { // 检查子类方法是否带有指定注解 boolean hasRequiredAnnotation = subclassMethod.getAnnotationMirrors().stream() .map(AnnotationMirror::getAnnotationType) .map(TypeMirror::toString) .anyMatch(annoType -> annoType.equals(requiredAnnotationName)); if (!hasRequiredAnnotation) { messager.printMessage(Diagnostic.Kind.ERROR, String.format("子类方法 %s.%s 重写父类方法时,必须添加注解 @%s", subclassType.getQualifiedName(), subclassMethod.getSimpleName(), requiredAnnotationClass.getSimpleName()), subclassMethod); } } } } } return true; } }
三、Gradle配置的关键修正
你在sample项目里只加了compileOnly依赖,但Java注解处理器需要单独配置依赖,Gradle中要使用annotationProcessor依赖来引入处理器,不然编译时不会触发这个处理器运行。
修正后的sample项目build.gradle:
plugins { id 'java' } group = 'org.bbloggsbott' version = '1.0-SNAPSHOT' repositories { mavenCentral() flatDir { dirs '<path to the annotation-enforcer jar>' } } dependencies { // 注解API依赖,用于在代码中使用@EnforceAnnotation compileOnly 'com.bbloggsbott:annotation-enforcer-1.0-SNAPSHOT' // 注解处理器依赖,让编译时自动执行处理器逻辑 annotationProcessor 'com.bbloggsbott:annotation-enforcer-1.0-SNAPSHOT' testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' } test { useJUnitPlatform() }
四、额外注意事项
确保你的注解处理器Jar正确打包:需要在Jar的META-INF/services/javax.annotation.processing.Processor文件中添加处理器的全类名org.bbloggsbott.annotationenforcer.EnforceAnnotationProcessor,这样Java编译器才能发现并加载这个处理器。如果你的处理器项目用Gradle构建,可以用java-library插件配合annotationProcessor配置自动生成这个文件,也可以手动添加。
现在重新编译sample项目,子类重写方法没加指定注解的话,就会触发编译错误啦~
备注:内容来源于stack exchange,提问作者BBloggsbott

