Vibesafe4j 使用 Annotation Processor 实现(编译时代码生成)

Vibesafe4j 默认在运行时生成代码,这种方式虽然灵活,但在生产环境中可能遇到一些问题:性能开销、调试困难、IDE 支持有限等。

Annotation Processor(注解处理器) 可以在编译时生成代码,解决这些问题。

编译时生成的优势

  • 性能更好:代码在编译时一次性生成,运行时无额外开销
  • 🐛 易于调试:生成的源代码文件可以直接查看和调试
  • 💡 IDE 友好:IDE 可以识别生成的代码,提供完整的代码补全
  • 提前发现问题:编译时就能发现错误,不用等到运行时
  • 🚀 生产环境友好:无需运行时编译器,部署更简单

什么时候用?

如果你需要:

  • 更好的性能表现
  • 可以查看和审查生成的代码
  • 完整的 IDE 支持
  • 适合生产环境使用

那么编译时生成是更好的选择。

什么是 Annotation Processor?

虽然 vibesafe4j 的当前实现使用运行时动态编译,但我们可以改用 Annotation Processor(注解处理器) 在编译时生成代码。Annotation Processor 是 Java 编译器的一个插件机制,允许在编译阶段处理注解并生成新的源代码文件。

这种方式相比运行时编译有以下优势:

Annotation Processor 的优势

  1. 编译时生成:代码在编译阶段生成,无需运行时编译器
  2. 性能更好:无需运行时编译和类加载开销
  3. 可调试性:生成的源代码文件可以直接查看和调试
  4. IDE 支持:更好的 IDE 集成和代码补全
  5. 类型安全:编译时就能发现类型错误
  6. 无运行时依赖:不需要 javac 在运行时可用

实现架构设计

方案一:编译时调用 AI(推荐用于开发环境)

在编译时直接调用 AI API 生成代码。这需要 Annotation Processor 能够进行网络请求。

方案二:预生成代码(推荐用于生产环境)

使用 Gradle/Maven 插件在编译前调用 AI 生成代码,然后 Annotation Processor 读取生成的代码。

项目模块结构

如果采用 Annotation Processor 方式,建议将项目拆分为两个模块:

模块划分

  1. vibesafe4j-annotations(注解模块)

    • 包含 @Func 注解定义
    • 轻量级,只包含注解接口
    • 用户项目需要依赖此模块
  2. vibesafe4j-processor(处理器模块)

    • 包含 Annotation Processor 实现
    • 包含 AI 代码生成器
    • 只在编译时使用,不会打包到最终应用

这种模块化设计的优势:

  • 依赖分离:注解模块轻量,处理器模块只在编译时需要
  • 性能优化:运行时不需要加载 Processor 相关代码
  • 清晰职责:注解和处理器职责分离,便于维护

注意:如果项目是单一模块,也可以将所有代码放在一个模块中,但需要确保 Processor 不会被打包到运行时依赖中。

完整实现代码

1. 修改 @Func 注解(支持编译时处理)

package vibesafe4j;

import java.lang.annotation.*;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.SOURCE)  // 改为 SOURCE,编译时处理
@Documented
public @interface Func {
    String value() default "";
    
    /**
     * 是否在编译时生成实现
     * 如果为 false,则跳过该方法的代码生成
     */
    boolean generate() default true;
}

2. 创建 Annotation Processor

package vibesafe4j.processor;

import com.google.auto.service.AutoService;
import vibesafe4j.Func;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.*;

/**
 * Annotation Processor for @Func annotation
 * 在编译时扫描 @Func 注解并生成实现类
 */
@SupportedAnnotationTypes("vibesafe4j.Func")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@AutoService(Processor.class)
public class FuncAnnotationProcessor extends AbstractProcessor {

    private Messager messager;
    private Filer filer;
    
    // AI 客户端接口(需要在编译时可用)
    private AiCodeGenerator aiCodeGenerator;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.filer = processingEnv.getFiler();
        
        // 初始化 AI 代码生成器
        // 可以通过系统属性或配置文件配置 AI API
        this.aiCodeGenerator = createAiCodeGenerator();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, 
                          RoundEnvironment roundEnv) {
        if (roundEnv.processingOver()) {
            return false;
        }

        // 按接口分组处理方法
        Map<TypeElement, List<ExecutableElement>> interfaceMethods = new HashMap<>();

        // 扫描所有带 @Func 注解的方法
        for (Element element : roundEnv.getElementsAnnotatedWith(Func.class)) {
            if (element.getKind() == ElementKind.METHOD) {
                ExecutableElement method = (ExecutableElement) element;
                TypeElement enclosingInterface = (TypeElement) method.getEnclosingElement();
                
                Func funcAnnotation = method.getAnnotation(Func.class);
                if (funcAnnotation.generate()) {
                    interfaceMethods.computeIfAbsent(enclosingInterface, k -> new ArrayList<>())
                                   .add(method);
                }
            }
        }

        // 为每个接口生成实现类
        for (Map.Entry<TypeElement, List<ExecutableElement>> entry : interfaceMethods.entrySet()) {
            TypeElement interfaceElement = entry.getKey();
            List<ExecutableElement> methods = entry.getValue();
            
            try {
                generateImplementation(interfaceElement, methods);
            } catch (IOException e) {
                messager.printMessage(Diagnostic.Kind.ERROR, 
                    "Failed to generate implementation for " + interfaceElement.getQualifiedName() + ": " + e.getMessage());
            }
        }

        return true;
    }

    /**
     * 生成接口实现类
     */
    private void generateImplementation(TypeElement interfaceElement, 
                                       List<ExecutableElement> methods) throws IOException {
        String packageName = getPackageName(interfaceElement);
        String interfaceName = interfaceElement.getSimpleName().toString();
        String implClassName = interfaceName + "Impl";
        String qualifiedImplName = packageName.isEmpty() 
            ? implClassName 
            : packageName + "." + implClassName;

        // 生成源代码
        StringBuilder sourceCode = new StringBuilder();
        
        // Package 声明
        if (!packageName.isEmpty()) {
            sourceCode.append("package ").append(packageName).append(";\n\n");
        }
        
        // 导入语句
        sourceCode.append("import java.util.*;\n");
        sourceCode.append("import java.time.*;\n\n");
        
        // 类声明
        sourceCode.append("/**\n");
        sourceCode.append(" * Auto-generated implementation of ").append(interfaceName).append("\n");
        sourceCode.append(" * Generated by vibesafe4j Annotation Processor\n");
        sourceCode.append(" */\n");
        sourceCode.append("public class ").append(implClassName)
                  .append(" implements ").append(interfaceName).append(" {\n\n");

        // 生成每个方法的实现
        for (ExecutableElement method : methods) {
            generateMethodImplementation(sourceCode, method);
        }

        sourceCode.append("}\n");

        // 写入文件
        JavaFileObject sourceFile = filer.createSourceFile(qualifiedImplName, interfaceElement);
        try (Writer writer = sourceFile.openWriter()) {
            writer.write(sourceCode.toString());
        }

        messager.printMessage(Diagnostic.Kind.NOTE, 
            "Generated implementation: " + qualifiedImplName);
    }

    /**
     * 生成单个方法的实现
     */
    private void generateMethodImplementation(StringBuilder sourceCode, 
                                             ExecutableElement method) {
        Func funcAnnotation = method.getAnnotation(Func.class);
        String prompt = buildPrompt(method, funcAnnotation.value());
        
        // 调用 AI 生成代码
        String methodBody = aiCodeGenerator.generateMethodBody(prompt, method);
        
        // 方法签名
        sourceCode.append("    @Override\n");
        sourceCode.append("    public ");
        
        // 返回类型
        TypeMirror returnType = method.getReturnType();
        sourceCode.append(getTypeName(returnType)).append(" ");
        
        // 方法名
        sourceCode.append(method.getSimpleName()).append("(");
        
        // 参数列表
        List<? extends VariableElement> parameters = method.getParameters();
        for (int i = 0; i < parameters.size(); i++) {
            VariableElement param = parameters.get(i);
            if (i > 0) {
                sourceCode.append(", ");
            }
            sourceCode.append(getTypeName(param.asType()))
                      .append(" ")
                      .append(param.getSimpleName());
        }
        
        sourceCode.append(") {\n");
        
        // 方法体(AI 生成)
        String indentedBody = indentMethodBody(methodBody);
        sourceCode.append(indentedBody);
        
        sourceCode.append("    }\n\n");
    }

    /**
     * 构建发送给 AI 的 prompt
     */
    private String buildPrompt(ExecutableElement method, String annotationValue) {
        StringBuilder prompt = new StringBuilder();
        
        prompt.append("Write a valid Java method body that implements the following signature:\n\n");
        prompt.append("Method signature: ");
        prompt.append(getTypeName(method.getReturnType())).append(" ");
        prompt.append(method.getSimpleName()).append("(");
        
        List<? extends VariableElement> params = method.getParameters();
        for (int i = 0; i < params.size(); i++) {
            if (i > 0) prompt.append(", ");
            VariableElement param = params.get(i);
            prompt.append(getTypeName(param.asType()))
                  .append(" ")
                  .append(param.getSimpleName());
        }
        prompt.append(")\n\n");
        
        if (!annotationValue.isEmpty()) {
            prompt.append("Requirements:\n");
            prompt.append(annotationValue).append("\n\n");
        }
        
        prompt.append("Requirements:\n");
        prompt.append("- Return only the method body code\n");
        prompt.append("- Do not include the method signature\n");
        prompt.append("- Do not include any comments or explanations\n");
        prompt.append("- Use proper Java syntax\n");
        
        return prompt.toString();
    }

    /**
     * 创建 AI 代码生成器
     * 可以通过系统属性配置
     */
    private AiCodeGenerator createAiCodeGenerator() {
        String apiKey = System.getProperty("vibesafe4j.ai.apiKey");
        String apiUrl = System.getProperty("vibesafe4j.ai.apiUrl", 
            "https://api.openai.com/v1/chat/completions");
        String model = System.getProperty("vibesafe4j.ai.model", "gpt-4");
        
        if (apiKey == null || apiKey.isEmpty()) {
            messager.printMessage(Diagnostic.Kind.WARNING,
                "No AI API key configured. Using mock generator. " +
                "Set -Avibesafe4j.ai.apiKey=your-key to enable AI code generation.");
            return new MockAiCodeGenerator();
        }
        
        return new OpenAiCodeGenerator(apiKey, apiUrl, model);
    }

    // 辅助方法
    private String getPackageName(TypeElement typeElement) {
        Element packageElement = typeElement.getEnclosingElement();
        while (packageElement != null && packageElement.getKind() != ElementKind.PACKAGE) {
            packageElement = packageElement.getEnclosingElement();
        }
        return packageElement != null ? packageElement.toString() : "";
    }

    private String getTypeName(TypeMirror type) {
        return type.toString();
    }

    private String indentMethodBody(String body) {
        return body.lines()
                   .map(line -> "        " + line)
                   .reduce((a, b) -> a + "\n" + b)
                   .orElse("");
    }
}

3. AI 代码生成器接口和实现

package vibesafe4j.processor;

import javax.lang.model.element.ExecutableElement;

/**
 * AI 代码生成器接口
 */
public interface AiCodeGenerator {
    /**
     * 根据 prompt 和方法签名生成方法体代码
     */
    String generateMethodBody(String prompt, ExecutableElement method);
}
package vibesafe4j.processor;

import javax.lang.model.element.ExecutableElement;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

/**
 * OpenAI API 实现
 */
public class OpenAiCodeGenerator implements AiCodeGenerator {
    
    private final String apiKey;
    private final String apiUrl;
    private final String model;
    private final HttpClient httpClient;

    public OpenAiCodeGenerator(String apiKey, String apiUrl, String model) {
        this.apiKey = apiKey;
        this.apiUrl = apiUrl;
        this.model = model;
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30))
            .build();
    }

    @Override
    public String generateMethodBody(String prompt, ExecutableElement method) {
        try {
            String requestBody = buildRequestBody(prompt);
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(apiUrl))
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();

            HttpResponse<String> response = httpClient.send(
                request, HttpResponse.BodyHandlers.ofString());

            return parseResponse(response.body());
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate code from AI", e);
        }
    }

    private String buildRequestBody(String prompt) {
        return String.format("""
            {
                "model": "%s",
                "messages": [
                    {
                        "role": "system",
                        "content": "You are a Java code generator. Return only valid Java code, no explanations."
                    },
                    {
                        "role": "user",
                        "content": "%s"
                    }
                ],
                "temperature": 0.3,
                "max_tokens": 1000
            }
            """, model, prompt.replace("\"", "\\\""));
    }

    private String parseResponse(String jsonResponse) {
        // 简化实现,实际应该使用 JSON 解析库
        int start = jsonResponse.indexOf("\"content\":\"") + 11;
        int end = jsonResponse.indexOf("\"", start);
        String content = jsonResponse.substring(start, end);
        return content.replace("\\n", "\n").replace("\\\"", "\"");
    }
}
package vibesafe4j.processor;

import javax.lang.model.element.ExecutableElement;

/**
 * Mock 实现,用于测试或当 AI API 不可用时
 */
public class MockAiCodeGenerator implements AiCodeGenerator {
    
    @Override
    public String generateMethodBody(String prompt, ExecutableElement method) {
        // 返回一个简单的实现
        return "throw new UnsupportedOperationException(\"Method not implemented\");";
    }
}

4. Maven 配置

注意vibesafe4j-annotationsvibesafe4j-processor 是推荐的模块化结构。如果项目是单一模块,可以将所有代码放在一个模块中,并相应调整依赖配置。

推荐的模块化结构:

<project>
    <!-- ... -->
    
    <dependencies>
        <!-- Annotation Processor 依赖 -->
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.1.1</version>
            <optional>true</optional>
        </dependency>
        
        <!-- 注解模块:包含 @Func 注解定义 -->
        <dependency>
            <groupId>vibesafe4j</groupId>
            <artifactId>vibesafe4j-annotations</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <annotationProcessorPaths>
                        <!-- Processor 模块:包含 Annotation Processor 实现 -->
                        <path>
                            <groupId>vibesafe4j</groupId>
                            <artifactId>vibesafe4j-processor</artifactId>
                            <version>1.0.0</version>
                        </path>
                        <path>
                            <groupId>com.google.auto.service</groupId>
                            <artifactId>auto-service</artifactId>
                            <version>1.1.1</version>
                        </path>
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <!-- 传递 AI API 配置 -->
                        <arg>-Avibesafe4j.ai.apiKey=${VIBESAFE4J_API_KEY}</arg>
                        <arg>-Avibesafe4j.ai.model=${VIBESAFE4J_MODEL:gpt-4}</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

模块说明:

  • vibesafe4j-annotations:包含 @Func 注解定义,运行时不需要,但编译时需要
  • vibesafe4j-processor:包含 Annotation Processor 实现,只在编译时使用,不会打包到最终应用中

如果使用单一模块:

如果项目是单一模块结构,可以将 Processor 配置为同一个 artifact:

<annotationProcessorPaths>
    <path>
        <groupId>vibesafe4j</groupId>
        <artifactId>vibesafe4j</artifactId>
        <version>1.0.0</version>
    </path>
</annotationProcessorPaths>

5. Gradle 配置

推荐的模块化结构:

dependencies {
    // Annotation Processor
    annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
    compileOnly 'com.google.auto.service:auto-service:1.1.1'
    
    // vibesafe4j 注解模块
    implementation 'vibesafe4j:vibesafe4j-annotations:1.0.0'
    // vibesafe4j Processor 模块(只在编译时使用)
    annotationProcessor 'vibesafe4j:vibesafe4j-processor:1.0.0'
}

tasks.withType(JavaCompile) {
    options.compilerArgs += [
        '-Avibesafe4j.ai.apiKey=' + (System.getenv('VIBESAFE4J_API_KEY') ?: ''),
        '-Avibesafe4j.ai.model=' + (System.getenv('VIBESAFE4J_MODEL') ?: 'gpt-4')
    ]
}

如果使用单一模块:

dependencies {
    annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
    // 单一模块时,使用同一个 artifact
    implementation 'vibesafe4j:vibesafe4j:1.0.0'
    annotationProcessor 'vibesafe4j:vibesafe4j:1.0.0'
}

6. 使用示例

接口定义保持不变:

package com.example;

import vibesafe4j.Func;

public interface Greeting {
    
    @Func("""
        return a greeting String
        
        >>> greet("Alice")
        'Hello, Alice!'
        """)
    String greet(String name);
}

编译后会自动生成 GreetingImpl.java

package com.example;

/**
 * Auto-generated implementation of Greeting
 * Generated by vibesafe4j Annotation Processor
 */
public class GreetingImpl implements Greeting {
    
    @Override
    public String greet(String name) {
        return "Hello, " + name + "!";
    }
}

7. Spring Boot 集成(简化版)

package vibesafe4j.spring;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * 自动配置:扫描生成的实现类并注册为 Bean
 */
@Configuration
@ConditionalOnClass(name = "vibesafe4j.Func")
public class Vibesafe4jAutoConfiguration {
    
    // 由于实现类已经在编译时生成,可以直接扫描
    // Spring 会自动发现并注册这些类
}

两种方案对比

特性运行时编译(当前)Annotation Processor(编译时)
代码生成时机运行时编译时
性能首次调用较慢编译时一次性生成
运行时依赖需要 javac无需编译器
可调试性难以调试可查看源代码
IDE 支持有限完整支持
AI 调用运行时调用编译时调用
离线编译支持需要网络(除非预生成)
错误发现运行时发现编译时发现

混合方案:预生成 + Annotation Processor

结合两种方式的优势:

  1. 开发阶段:使用 Gradle/Maven 插件在编译前调用 AI 生成代码
  2. Annotation Processor:读取预生成的代码并生成实现类
  3. 生产环境:使用预生成的代码,无需 AI API

Gradle 插件示例

// build.gradle
plugins {
    id 'java'
    id 'vibesafe4j' version '1.0.0'
}

vibesafe4j {
    apiKey = project.findProperty('vibesafe4j.apiKey') ?: System.getenv('VIBESAFE4J_API_KEY')
    model = 'gpt-4'
    outputDir = file('src/generated/vibesafe4j')
}

sourceSets {
    main {
        java {
            srcDirs += 'src/generated/vibesafe4j'
        }
    }
}

实现总结

使用 Annotation Processor 实现 vibesafe4j 的主要改动:

  1. 注解保留策略@Retention(RetentionPolicy.SOURCE)
  2. 创建 Processor:实现 AbstractProcessor 接口
  3. 代码生成:使用 Filer.createSourceFile() 生成源代码
  4. AI 集成:在编译时调用 AI API(或使用预生成代码)
  5. 构建配置:配置 Annotation Processor 路径和参数

这种方式更适合生产环境,提供了更好的性能和可维护性。

常见问题(FAQ)

Q: Annotation Processor 和运行时编译有什么区别?
A: Annotation Processor 在编译时生成代码,性能更好,可调试性强;运行时编译在程序运行时生成代码,更灵活但性能较差。

Q: 如何在编译时调用 AI API?
A: 可以在 Annotation Processor 中使用 HTTP 客户端调用 AI API,或使用预生成代码方案。

Q: vibesafe4j-annotationsvibesafe4j-processor 是两个独立的项目吗?
A: 这是推荐的模块化结构。vibesafe4j-annotations 包含注解定义,vibesafe4j-processor 包含处理器实现。如果项目是单一模块,也可以将两者合并,但需要确保 Processor 不会被打包到运行时依赖中。

Q: 生成的代码在哪里?
A: 生成的代码通常在 target/generated-sources/annotations(Maven)或 build/generated/sources/annotationProcessor(Gradle)目录。

Q: IDE 能识别生成的代码吗?
A: 可以,现代 IDE(IntelliJ IDEA、Eclipse)都能自动识别和索引生成的代码。

Q: 如何调试 Annotation Processor?
A: 可以使用 messager.printMessage() 输出调试信息,或使用 IDE 的远程调试功能。

相关文章

参考资源


本文详细介绍了如何使用 Annotation Processor 实现 vibesafe4j 的编译时代码生成功能。通过编译时生成代码,可以获得更好的性能和可维护性。