You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

注解处理器编译时未触发,存在注解误用类

自定义注解处理器未触发问题

我在练习自定义数据结构时,定义了Implementation注解,用于标记抽象实现类或具体实现类:当标记为具体实现时,要求类继承AbstractDataEngine并提供实现Iterator的内部类。


1. Implementation注解定义

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * A marker annotation, forces the class which is to be defined as a data engine, to
 * extend {@code AbstractDataEngine} or any of its subclasses and provide an {@code Iterator}
 * implementation.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Implementation {

    ImplementationType value();

    enum ImplementationType {
        ABSTRACTION, IMPLEMENTATION;

        @Override
        public String toString() {
            return switch (this){
                case ABSTRACTION -> "abstraction";
                case IMPLEMENTATION -> "implementation";
            };
        }
    }
}

2. AbstractDataEngine类定义

import java.util.concurrent.atomic.AtomicReference;

/**
 * This class defines methods, that will be common to every single data engine, irrespective of type of
 * data structure, or its implementation
 * @param <E> Type of data being stored
 */
@Implementation(Implementation.ImplementationType.ABSTRACTION)
public abstract class AbstractDataEngine<E> implements DataEngine<E>, Iterable<E>{

    //The golden ratio,, generally used as a growth factor for determining new size
    protected final double GOLDEN_RATIO = 1.61803398875;

    //Only for array-based
    protected final double DEFAULT_CAPACITY = 16;
    protected final double GROWTH_LOAD_FACTOR = 0.75;
    protected final double SHRINK_LOAD_FACTOR = 0.25;

    protected int activeSize;
    protected int maxCapacity;

    protected AbstractDataEngine(int activeSize, int maxCapacity){
        this.activeSize = activeSize;
        this.maxCapacity = maxCapacity;
    }

    /**
     * When invoked on a data engine that implements an underlying array, will shift all the elements
     * to the beginning, i.e. a sparsely populated array can be so adjusted that all the elements
     * get move to the front
     *
     */
    protected abstract void compress();

    /**
     * When the {@code activeSize} is less than {@code SHRINK_LOAD_FACTOR * maxCapacity}, for an
     * underlying array it will end up shrinking by {@code maxCapacity - Math.floor(GOLDEN_RATIO * maxCapacity)}
     * Cam have an asynchronous implementation in thread-safe data engines.
     *
     */
    protected abstract void shrink();

    /**
     * When the {@code activeSize} is greater than {@code GROWTh_LOAD_FACTOR * maxCapacity}, for an
     * underlying array it will end up growing by {@code maxCapacity - Math.floor(GOLDEN_RATIO * maxCapacity)}
     * Cam have an asynchronous implementation in thread-safe data engines.
     *
     */
    protected abstract void grow();

    /**
     * @return Returns true, if the invoking data engine is empty, false otherwise
     */
    @Override
    public boolean isEmpty() {
        return this.activeSize == 0;
    }

    /**
     * @return Returns max capacity of the data engine, or the max size before growth
     */
    @Override
    public int maxCapacity() {
        return this.maxCapacity;
    }

    /**
     * @return Returns current size of data engine
     */
    @Override
    public int getActiveSize() {
        return this.activeSize;
    }

    /**
     * Generates an {@code AtomicReference} of the data engine. Useful when the object has to be
     * used in a thread blocking context without making the data engine asynchronous.
     *
     * @return Returns an {@code AtomicReference} of the invoking data engine
     * @throws UnsupportedOperationException Will throw an exception when the invoking data engine is
     *                                       already {@code Thread-Mutable}
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T extends DataEngine<E>> AtomicReference<T> createThreadSafeImage() {
        return new AtomicReference<>((T)this);
    }

    /**
     * The method reverses the invoking data engine when implemented.
     *
     * @throws UnsupportedOperationException Thrown when it is unimplemented.
     */
    public abstract void reverse();
}

3. 注解处理器代码

package data.core;

import com.google.auto.service.AutoService;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import java.util.Iterator;
import java.util.Set;
import javax.lang.model.element.Modifier;

@AutoService(javax.annotation.processing.Processor.class)
@SupportedAnnotationTypes("de.core.Implementation")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class ImplementationAnnotationProcessor extends AbstractProcessor {

    private Messager messager;

    // Initialize core components to be used during processing
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Diagnostic.Kind.NOTE, "Hitting the processor");
        for (Element element : roundEnv.getElementsAnnotatedWith(Implementation.class)) {

            //If the element annotated is not a class type
            if(!element.getKind().isClass()) {

                messager.printMessage(Diagnostic.Kind.ERROR,
                        "Implementation must be used on a class", element);
                return true;
            }else if(element.getAnnotation(Implementation.class).value()
                    .equals(Implementation.ImplementationType.ABSTRACTION)
            & !element.getModifiers().contains(Modifier.ABSTRACT)){

                //Invalid abstraction definition
                messager.printMessage(Diagnostic.Kind.ERROR,
                        "An Implementation defined as an ABSTRACTION" +
                        "must be declared abstract", element);
                return true;
            }else if(element.getAnnotation(Implementation.class).value()
                    .equals(Implementation.ImplementationType.IMPLEMENTATION)
            & element.getModifiers().contains(Modifier.ABSTRACT)){

                //Invalid implementation definition
                messager.printMessage(Diagnostic.Kind.ERROR,
                        "An implementation defined as an IMPLEMENTATION" +
                                "cannot be declared abstract", element);
                return true;
            }else if(element.getAnnotation(Implementation.class).value()
                    .equals(Implementation.ImplementationType.IMPLEMENTATION)){

                //All exceptional conditions removed, now checking the specifics
                //If the class does not extend an abstraction
                if(!isSubClassOf((TypeElement) element)){
                    messager.printMessage(Diagnostic.Kind.ERROR,
                            "An implementation must extend "+ AbstractDataEngine.class +
                            " to be considered a valid implementation", element);
                    return true;
                }

                if(!hasEnclosedIterator((TypeElement) element)){
                    messager.printMessage(Diagnostic.Kind.ERROR, "Implementation does not contain a" +
                            "Iterator implementation", element);
                    return true;
                }
            }
        }
        return true;
    }


    private boolean isSubClassOf(TypeElement element) {
        TypeMirror superClassType = element.getSuperclass();

        while (superClassType.getKind() != TypeKind.NONE){
            if(AbstractDataEngine.class.toString().equals(superClassType.getClass().toString())){
                return true;
            }
            superClassType = ((TypeElement) processingEnv.getTypeUtils().asElement(superClassType))
                    .getSuperclass();
        }

        return false;
    }

    private boolean hasEnclosedIterator(TypeElement element) {
        for (Element enclosedElement : element.getEnclosedElements()) {
            if (enclosedElement.getKind().isClass()) {
                for (TypeMirror inf : element.getInterfaces()) {
                    if (inf.toString().equals(Iterator.class.getCanonicalName()))
                        return true;
                }
            }
        }
        return false;
    }
}

4. 测试类(应触发异常的AbstractList)

package engine.core;

import data.core.Implementation;

/**
 * The top-level abstract class for all List implementations, including thread-safe
 * implementations. It defines methods specific to the behaviour of a list, unique value lists are also considered.
 * @param <E>
 */
@Implementation(Implementation.ImplementationType.ABSTRACTION)
public class AbstractList<E> {

}

问题现状

我使用AutoService注册处理器,采用Maven构建(IntelliJ环境)。最初所有文件在一个项目中,配置Maven在编译时引入处理器,但编译无错误;后来将核心文件拆分到独立模块编译为Jar,在另一个模块引用,处理器仍未触发。

内容的提问来源于stack exchange,提问作者DevSw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 04:19:49