自定義註解處理器自動生成META-INF/spring.factories文件

之前研究java的註解處理器,使用了谷歌的AutoService,可以自動生成 META-INF/services 文件夾中定義的類。

最近自己寫spring boot starter,也需要配置自定義的SPI實現到META-INF/spring.factories,所以聯想到自己是不是也可以寫一個註解處理器來自動生成配置文件

首先還是先引入autoservice的依賴包

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <encoding>UTF-8</encoding>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <auto-service.version>1.0-rc6</auto-service.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service-annotations</artifactId>
        <version>${auto-service.version}</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service</artifactId>
        <version>${auto-service.version}</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

定義自定義生成spring配置文件的註解

@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface SpringAutoService {
    Class<?>[] value();
}

實現自定義的註解處理器

private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (roundEnv.processingOver()) {
        //等註解全部解析完,最後生成配置文件
        generateConfigFiles();
    } else {
        //解析SpringAutoService註解標註的類信息,保存到providers        processAnnotations(annotations, roundEnv);
    }

    return true;
}

解析註解的信息,保存起來

private void processAnnotations(Set<? extends TypeElement> annotations,
                                RoundEnvironment roundEnv) {

    Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(SpringAutoService.class);

    for (Element e : elements) {
        TypeElement providerImplementer = (TypeElement) e;
        AnnotationMirror annotationMirror = getAnnotationMirror(e, SpringAutoService.class).get();
        Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
        if (providerInterfaces.isEmpty()) {
            error(MISSING_SERVICES_ERROR, e, annotationMirror);
            continue;
        }
        for (DeclaredType providerInterface : providerInterfaces) {
            TypeElement providerType = MoreTypes.asTypeElement(providerInterface);
            //檢查註解的class類型是否有繼承關係
            if (checkImplementer(providerImplementer, providerType)) {
                providers.computeIfAbsent(getBinaryName(providerType), key -> new HashSet<>())
                        .add(getBinaryName(providerImplementer));
            } else {
                String message = "ServiceProviders must implement their service provider interface. "
                        + providerImplementer.getQualifiedName() + " does not implement "
                        + providerType.getQualifiedName();
                error(message, e, annotationMirror);
            }
        }
    }
}

註解全部解析完成以後,再生成文件

private void generateConfigFiles() {
    Filer filer = processingEnv.getFiler();

    try {
        FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
                SERVICES_PATH);
        OutputStream out = fileObject.openOutputStream();
        SpringServicesFiles.writeServiceFile(providers, out);
        out.close();
        log("Wrote to: " + fileObject.toUri());
    } catch (IOException e) {
        fatalError("Unable to create " + SERVICES_PATH + ", " + e);
        return;
    }
}

具體生成配置文件的邏輯

static void writeServiceFile(Map<String, Set<String>> services, OutputStream output) throws IOException {
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, Charsets.UTF_8));
    for (Map.Entry<String, Set<String>> entry : services.entrySet()) {
        writer.write(entry.getKey() + "=\\");

        boolean first = true;
        for (String service : entry.getValue()) {
            if (first) {
                writer.newLine();
                writer.write(service);
                first = false;
            } else {
                writer.write(",\\");
                writer.newLine();
                writer.write(service);
            }
        }
        writer.newLine();
    }
    writer.flush();
}

具體使用可以直接通過註解@ SpringAutoService ,編譯完成以後就可以自動生成 META-INF/spring.factories 文件

引入依賴,需要先在本地將SpringAutoService的項目在本地install

<dependency>
    <groupId>git.oschina.dushougudu</groupId>
    <artifactId>spring-auto-service-annotations</artifactId>
    <version>1.0</version>
    <scope>compile</scope>
</dependency>

示例

package com.starter;

import git.oschina.dushougudu.annotation.SpringAutoService; 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration;

@Configuration @SpringAutoService(EnableAutoConfiguration.class) public class MyStarterAutoConfiguration {

 // … }

項目地址:https://gitee.com/dushougudu/spring-auto-service-annotations

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章