• 【Android】Google AutoService的使用与源码解析


    AutoService是Google开发一个自动生成SPI清单文件的框架。看过一些基于APT的三方框架源码的读者应该有所了解。比如Arouter、EventBus等等。一般我们用它来自动帮我们注册APT文件(全称是Annotation Process Tool,或者叫注解处理器,AbstractProcessor的实现)。很多生成SPI文件的框架也是抄袭它的源码,可见它的作用还不小。

    APT其实就是基于SPI一个工具,是JDK留给开发者的一个在编译前处理注解的接口。APT也是SPI的一个应用。关于SPI和APT下文会详细讲到。

    先讲一下它是如何使用的。

    AutoService的使用

    AutoService框架的作用是自动生成SPI清单文件(META-INF/services下的文件)。不用它也行,如果不使用它就需要手动去创建这个文件、手动往这个文件里添加服务(接口实现)。

    AutoService比较常用的场景是帮助注册APT(注解处理器)。下面以APT的例子来讲解它的使用。

    开发APT需要在Java SE项目中开发,因为需要继承AbstractProcessor,AbstractProcessor作用在Java编译阶段。

    先创建Java module,在Android Studio中也可以创建,然后在build.gradle中添加依赖,如下dependencies部分。

    通过annotationProcessor添加注解处理器(AutoServiceProcessor.class),同时需要通过implementation添加annotation依赖,即AutoService.class。

    plugins {
        id 'java-library'
    }
    
    dependencies {
        annotationProcessor 'com.google.auto.service:auto-service:1.0.1'
    	//一般结合JavaPoet框架来生成Java代码,这里不对它进行阐述。
    	//implementation 'com.squareup:javapoet:1.13.0' 
        implementation 'com.google.auto.service:auto-service-annotations:1.0.1'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后在你处理注解处理器类上方添加@AutoService注解即可,value指定成javax.annotation.processing.Processor类,因为要生成的SPI清单文件(META-INF/services下的文件)名称是
    javax.annotation.processing.Processor 这个Processor是Java内置的,Javac编译前默认的注解处理器接口。如果是我们自定义的接口就指定成自己的接口名。

    @AutoService(value = {Processor.class})
    public class MyProcessor extends AbstractProcessor {
        @Override
        public synchronized void init(ProcessingEnvironment processingEnv) {
            System.out.println("MyProcessor------------init---------------");
            super.init(processingEnv);
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            System.out.println("MyProcessor------------process---------------");
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    AbstractProcessor是继承自Processor接口:

    public abstract class AbstractProcessor implements Processor {
    	...
    }
    
    • 1
    • 2
    • 3

    AbstractProcessor这个类是JDK SE中的,Android Framework将它删除了(因为不需要也用不着),所以Android Module里面是不存在的。这也说明为什么创建Java SE项目来编写APT代码。

    AutoService注解的声明如下,它的value是一个class集合,可以指定多个value。

    @Documented
    @Retention(CLASS)
    @Target(TYPE)
    public @interface AutoService {
      /** Returns the interfaces implemented by this service provider. */
      Class<?>[] value();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    以上示例中MyProcessor的作用是处理项目的自定义注解,比如Arouter框架会利用它来处理@Aouter注解,并自动生成路由注册类。

    编译这个Java项目后就会自动将MyProcessor添加到APT的SPI注册文件中。

    要注意的是,这个时候MyProcessor是没有起作用的,init和process方法都不会执行。因为注解处理阶段它并不在SPI注册文件中,注解处理阶段完成后它才注册进去。将Java项目打包成jar,这个MyProcessor才会在SPI注册文件中。别的项目依赖这个jar,MyProcessor的代码才会执行。

    以上是AutoService的使用。讲了这些,可能有人看不懂。没关系,先了解一下SPI技术。

    关于SPI

    什么是SPI呢,了解SPI是读懂AutoService的基础。

    SPI是Service Provider Interface的简称,是JDK默认提供的一种将接口和实现类进行分离的机制。这种机制能将接口和实现进行解耦,大大提升系统的可扩展性。

    SPI机制约定:当一个Jar包需要提供一个接口的实现类时,这个Jar包需要在META-INF/services目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该Jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

    SPI示例

    比如有一个接口IMyService

    package com.devnn.demo.interface
      
    public interface IMyService {
    
        void hello();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    它的实现类有:

    package com.devnn.demo.impl
    import com.devnn.demo.interfaces.devnnService;
      
    public class MyServiceImpl_1 implements IMyService {
    
        @Override
        public void hello() {
            System.out.println("Hi,I am MyServiceImpl_1");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    package com.devnn.demo.impl;
    import com.devnn.demo.interfaces.devnnService;
    
    public class MyServiceImpl_2 implements IMyService {
        @Override
        public void hello() {
            System.out.println("Hi,I am MyServiceImpl_2");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    resource/META-INF/services目录下创建文件com.devnn.demo.interface.IMyService,内容为所有实现类的完整名称:

    com.devnn.demo.impl.MyServiceImpl_1
    com.devnn.demo.impl.MyServiceImpl_2
    
    • 1
    • 2

    项目结构:
    在这里插入图片描述

    加载IMyService接口的所有子类:

    public class SPI_Demo {
      
        public static void main(String[] agrs) {
          
           //使用jdk提供的类ServiceLoader来加载IMyService的子类
           ServiceLoader<IMyService> loaders = ServiceLoader.load(IMyService.class);
          
           //遍历并调用子类方法
            for (IMyService service : loaders) { 
                service.hello();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行就会打印:

    Hi,I am MyServiceImpl_1
    Hi,I am MyServiceImpl_2
    
    • 1
    • 2

    是不是很神奇,通过一个接口,就可以找到它的实现类,这就是SPI的作用。

    APT技术

    然后再说下APT,开头说了APT是SPI的一个应用。为什么这么说呢?APT其实就是Java给我们提供的内置的SPI接口,作用是在编译java前处理java源码中的注解。
    APT的服务接口就是这个
    javax.annotation.processing.Processor
    跟META_INF/service下的文件名是一致的。

    Java编译器读取这个清单文件,加载实现这个接口的所有类,完成用户的注解处理逻辑。

    AutoService源码

    然后再回到AutoService,结合源码对它进行剖析,AutoService主要代码就一个类,即AutoServiceProcessor.java,为了方便阅读,笔者先将它原封不动copy在这里,后面再对它进行解析。

    /*
     * Copyright 2008 Google LLC
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.google.auto.service.processor;
    
    import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
    import static com.google.auto.common.MoreElements.getAnnotationMirror;
    import static com.google.auto.common.MoreStreams.toImmutableSet;
    import static com.google.common.base.Throwables.getStackTraceAsString;
    
    import com.google.auto.common.MoreElements;
    import com.google.auto.common.MoreTypes;
    import com.google.auto.service.AutoService;
    import com.google.common.annotations.VisibleForTesting;
    import com.google.common.collect.HashMultimap;
    import com.google.common.collect.ImmutableList;
    import com.google.common.collect.ImmutableSet;
    import com.google.common.collect.Multimap;
    import com.google.common.collect.Sets;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import java.util.SortedSet;
    import javax.annotation.processing.AbstractProcessor;
    import javax.annotation.processing.Filer;
    import javax.annotation.processing.RoundEnvironment;
    import javax.annotation.processing.SupportedOptions;
    import javax.lang.model.SourceVersion;
    import javax.lang.model.element.AnnotationMirror;
    import javax.lang.model.element.AnnotationValue;
    import javax.lang.model.element.Element;
    import javax.lang.model.element.PackageElement;
    import javax.lang.model.element.TypeElement;
    import javax.lang.model.type.DeclaredType;
    import javax.lang.model.type.TypeMirror;
    import javax.lang.model.util.SimpleAnnotationValueVisitor8;
    import javax.lang.model.util.Types;
    import javax.tools.Diagnostic.Kind;
    import javax.tools.FileObject;
    import javax.tools.StandardLocation;
    
    /**
     * Processes {@link AutoService} annotations and generates the service provider
     * configuration files described in {@link java.util.ServiceLoader}.
     * 

    * Processor Options:

      *
    • {@code -Adebug} - turns on debug statements
    • *
    • {@code -Averify=true} - turns on extra verification
    • *
    */
    @SupportedOptions({"debug", "verify"}) public class AutoServiceProcessor extends AbstractProcessor { @VisibleForTesting static final String MISSING_SERVICES_ERROR = "No service interfaces provided for element!"; private final List<String> exceptionStacks = Collections.synchronizedList(new ArrayList<>()); /** * Maps the class names of service provider interfaces to the * class names of the concrete classes which implement them. *

    * For example, * {@code "com.google.apphosting.LocalRpcService" -> * "com.google.apphosting.datastore.LocalDatastoreService"} */ private final Multimap<String, String> providers = HashMultimap.create(); @Override public ImmutableSet<String> getSupportedAnnotationTypes() { return ImmutableSet.of(AutoService.class.getName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** *

      *
    1. For each class annotated with {@link AutoService}
        *
      • Verify the {@link AutoService} interface value is correct *
      • Categorize the class by its service interface *
      * *
    2. For each {@link AutoService} interface
        *
      • Create a file named {@code META-INF/services/} *
      • For each {@link AutoService} annotated class for this interface
          *
        • Create an entry in the file *
        *
      *
    */
    @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { try { processImpl(annotations, roundEnv); } catch (RuntimeException e) { // We don't allow exceptions of any kind to propagate to the compiler String trace = getStackTraceAsString(e); exceptionStacks.add(trace); fatalError(trace); } return false; } ImmutableList<String> exceptionStacks() { return ImmutableList.copyOf(exceptionStacks); } private void processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { generateConfigFiles(); } else { processAnnotations(annotations, roundEnv); } } private void processAnnotations( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class); log(annotations.toString()); log(elements.toString()); for (Element e : elements) { // TODO(gak): check for error trees? TypeElement providerImplementer = MoreElements.asType(e); AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.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); log("provider interface: " + providerType.getQualifiedName()); log("provider implementer: " + providerImplementer.getQualifiedName()); if (checkImplementer(providerImplementer, providerType, annotationMirror)) { providers.put(getBinaryName(providerType), 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(); for (String providerInterface : providers.keySet()) { String resourceFile = "META-INF/services/" + providerInterface; log("Working on resource file: " + resourceFile); try { SortedSet<String> allServices = Sets.newTreeSet(); try { // would like to be able to print the full path // before we attempt to get the resource in case the behavior // of filer.getResource does change to match the spec, but there's // no good way to resolve CLASS_OUTPUT without first getting a resource. FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile); log("Looking for existing resource file at " + existingFile.toUri()); Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream()); log("Existing service entries: " + oldServices); allServices.addAll(oldServices); } catch (IOException e) { // According to the javadoc, Filer.getResource throws an exception // if the file doesn't already exist. In practice this doesn't // appear to be the case. Filer.getResource will happily return a // FileObject that refers to a non-existent file but will throw // IOException if you try to open an input stream for it. log("Resource file did not already exist."); } Set<String> newServices = new HashSet<>(providers.get(providerInterface)); if (!allServices.addAll(newServices)) { log("No new service entries being added."); continue; } log("New service file contents: " + allServices); FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile); try (OutputStream out = fileObject.openOutputStream()) { ServicesFiles.writeServiceFile(allServices, out); } log("Wrote to: " + fileObject.toUri()); } catch (IOException e) { fatalError("Unable to create " + resourceFile + ", " + e); return; } } } /** * Verifies {@link ServiceProvider} constraints on the concrete provider class. Note that these * constraints are enforced at runtime via the ServiceLoader, we're just checking them at compile * time to be extra nice to our users. */ private boolean checkImplementer( TypeElement providerImplementer, TypeElement providerType, AnnotationMirror annotationMirror) { String verify = processingEnv.getOptions().get("verify"); if (verify == null || !Boolean.parseBoolean(verify)) { return true; } // TODO: We're currently only enforcing the subtype relationship // constraint. It would be nice to enforce them all. Types types = processingEnv.getTypeUtils(); if (types.isSubtype(providerImplementer.asType(), providerType.asType())) { return true; } // Maybe the provider has generic type, but the argument to @AutoService can't be generic. // So we allow that with a warning, which can be suppressed with @SuppressWarnings("rawtypes"). // See https://github.com/google/auto/issues/870. if (types.isSubtype(providerImplementer.asType(), types.erasure(providerType.asType()))) { if (!rawTypesSuppressed(providerImplementer)) { warning( "Service provider " + providerType + " is generic, so it can't be named exactly by @AutoService." + " If this is OK, add @SuppressWarnings(\"rawtypes\").", providerImplementer, annotationMirror); } return true; } return false; } private static boolean rawTypesSuppressed(Element element) { for (; element != null; element = element.getEnclosingElement()) { SuppressWarnings suppress = element.getAnnotation(SuppressWarnings.class); if (suppress != null && Arrays.asList(suppress.value()).contains("rawtypes")) { return true; } } return false; } /** * Returns the binary name of a reference type. For example, * {@code com.google.Foo$Bar}, instead of {@code com.google.Foo.Bar}. * */ private String getBinaryName(TypeElement element) { return getBinaryNameImpl(element, element.getSimpleName().toString()); } private String getBinaryNameImpl(TypeElement element, String className) { Element enclosingElement = element.getEnclosingElement(); if (enclosingElement instanceof PackageElement) { PackageElement pkg = MoreElements.asPackage(enclosingElement); if (pkg.isUnnamed()) { return className; } return pkg.getQualifiedName() + "." + className; } TypeElement typeElement = MoreElements.asType(enclosingElement); return getBinaryNameImpl(typeElement, typeElement.getSimpleName() + "$" + className); } /** * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code * annotationMirror}. */ private ImmutableSet<DeclaredType> getValueFieldOfClasses(AnnotationMirror annotationMirror) { return getAnnotationValue(annotationMirror, "value") .accept( new SimpleAnnotationValueVisitor8<ImmutableSet<DeclaredType>, Void>(ImmutableSet.of()) { @Override public ImmutableSet<DeclaredType> visitType(TypeMirror typeMirror, Void v) { // TODO(ronshapiro): class literals may not always be declared types, i.e. // int.class, int[].class return ImmutableSet.of(MoreTypes.asDeclared(typeMirror)); } @Override public ImmutableSet<DeclaredType> visitArray( List<? extends AnnotationValue> values, Void v) { return values.stream() .flatMap(value -> value.accept(this, null).stream()) .collect(toImmutableSet()); } }, null); } private void log(String msg) { if (processingEnv.getOptions().containsKey("debug")) { processingEnv.getMessager().printMessage(Kind.NOTE, msg); } } private void warning(String msg, Element element, AnnotationMirror annotation) { processingEnv.getMessager().printMessage(Kind.WARNING, msg, element, annotation); } private void error(String msg, Element element, AnnotationMirror annotation) { processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation); } private void fatalError(String msg) { processingEnv.getMessager().printMessage(Kind.ERROR, "FATAL ERROR: " + msg); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342

    AutoService源码分析

    主要逻辑在process方法中,通过实现AbstractProcessor的process方法来实现功能。

    process委托给了processImpl:

     private void processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       if (roundEnv.processingOver()) { //本轮注解处理完毕
          generateConfigFiles();//生成SPI注册文件
        } else { //未处理完毕,继续处理
          processAnnotations(annotations, roundEnv);//整理需要注册的文件,放入缓存
        }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    再看processAnnotations方法,笔者已经加了注释:

    private void processAnnotations(
          Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    
    	//获取所有加了AutoService注解的类
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
        
        for (Element e : elements) {
          //将Element转成TypeElement
          TypeElement providerImplementer = MoreElements.asType(e);
          //获取AutoServce注解指定的value
          AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get();
          //获取value集合
          Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
          //如果没有指定value,报错
          if (providerInterfaces.isEmpty()) {
            error(MISSING_SERVICES_ERROR, e, annotationMirror);
            continue;
          }
          //遍历所有的value,获取value的完整类名(例如javax.annotation.processing.Processor)
          for (DeclaredType providerInterface : providerInterfaces) {
            TypeElement providerType = MoreTypes.asTypeElement(providerInterface);
    
    		//判断是否是继承关系,是则放入providers缓存起来,否则报错
            if (checkImplementer(providerImplementer, providerType, annotationMirror)) {
              providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
            } else {
             //报错代码,略
            }
          }
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    注解处理完毕,就会生成SPI注册文件。如果SPI路径上文件已经存在,先要把已存在的SPI清单读进内存,再把新的provider加进去,然后全部写出,覆盖原来的文件。这部分逻辑如下:

      private void generateConfigFiles() {
        Filer filer = processingEnv.getFiler();//获取文件工具类,processingEnv是AbstractProcessor的成员变量,直接拿来用。
    
    	//遍历之前解析的providers缓存
        for (String providerInterface : providers.keySet()) {
         //providerInterface就是value字段指定的接口,例如javax.annotation.processing.Processor
          String resourceFile = "META-INF/services/" + providerInterface;
          log("Working on resource file: " + resourceFile);
          try {
            SortedSet<String> allServices = Sets.newTreeSet();
            try {
            //已经存在的SPI文件
              FileObject existingFile =
                  filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
              //SPI文件中的service条目清单
              Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
              log("Existing service entries: " + oldServices);
              allServices.addAll(oldServices);
            } catch (IOException e) {
              log("Resource file did not already exist.");
            }
    
    		//新的service条目清单
            Set<String> newServices = new HashSet<>(providers.get(providerInterface));
            //如果已经存在,则不处理
            if (!allServices.addAll(newServices)) {
              log("No new service entries being added.");
              continue;
            }
    		//以下是将缓存的services写入文件中。
            log("New service file contents: " + allServices);
            FileObject fileObject =
                filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
            try (OutputStream out = fileObject.openOutputStream()) {
              ServicesFiles.writeServiceFile(allServices, out);
            }
            log("Wrote to: " + fileObject.toUri());
          } catch (IOException e) {
            fatalError("Unable to create " + resourceFile + ", " + e);
            return;
          }
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    可见AutoServiceProcessor的主要功能就是将加了AutoService注解的类,加到SPI注册文件中。SPI文件名称(或者叫服务)可以通过value指定。

    下面将AutoService从mavenCentral仓库中下载下来(一个jar包),解压查看它的内容:
    在这里插入图片描述

    可以看到它里面内容并不多,主要就是一个AutoServiceProcessor类和一个APT清单文件。打开这个清单文件,里面就是AutoServiceProcessor类的全路径:

    在这里插入图片描述

    所以我们将AutoService加到java项目中,其实就是引入了AutoServiceProcessor这个注解处理器,帮助我们处理@AutoService注解,将我们的服务(一般是APT类,也可以是其它的类,通过value指定)自动注册进SPI文件中。

    看到这里,不知道读者有没有领悟。

    AutoService是一个注解处理器,我们自己开发的APT也是注解处理器,它们都是注解处理器,AutoSevice是自动帮我们注册注解处理器的注解处理器。是不是有点绕?

    当然AutoService的作用不仅在于注册APT,还可以注册其它服务。只是注册APT我们比较常见。

    再举一个AutoService的使用场景:

    在组件化架构app中,有一个主Module和若干业务Module,如何在主Module中初始化各个业务Module?这可以使用SPI技术,在业务Module中创建一个初始化类实现一个共同的接口,然后在这个类上加AutoService注解,在主Module中就可以通过SPI机制加载这些业务Module的初始化类,调用初始化接口。

    AutoService不仅是一个自动注册APT的框架,它还是一个SPI技术的模板,有时候我们需要自己开发一个基于APT同时又要注册自定义service的框架,它的源码是一个很好的参考。AutoServiceProcessor里面的大部分代码是可以复制拿来用。再比如,ServiceFiles.java是SPI资源文件读取和写入的工具类,直接复制到我们项目中即可。

  • 相关阅读:
    H3C交换机如何重置console口密码
    conventional-changelog standard-version 配置项
    服务名无效。 请键入 NET HELPMSG 2185以获得更多的帮助
    Intel汇编-内联汇编使用寄存器
    Vite搭建Vue3 + TypeScript 项目(Yarn)
    Nginx:负载均衡
    操作系统05-并发与同步
    除了「加机器」,其实你的微服务还能这样优化
    17.11 std::atomic续谈、std::async深入谈
    SQL常用脚本整理,建议收藏
  • 原文地址:https://blog.csdn.net/devnn/article/details/126837081