java注解处理器之Google Auto Service

介绍

SPI 是 java 提供的一种服务发现的标准,具体请看SPI 介绍,但每次我们都需要自己创建 services 目录,以及配置文件,google 的 autoservice 就可以帮我们省去这一步。

使用

maven 的依赖

复制<dependency>
      <groupId>com.google.auto.service</groupId>
      <artifactId>auto-service-annotations</artifactId>
      <version>1.0-rc6</version>
      <optional>true</optional>
      <scope>compile</scope>
</dependency>
<dependency>
      <groupId>com.google.auto.service</groupId>
      <artifactId>auto-service</artifactId>
      <version>1.0-rc6</version>
      <optional>true</optional>
      <scope>compile</scope>
</dependency>

定义接口

public interface UserService {

String userName();
}

定义接口实现,使用 AutoService 注解

@AutoService(UserService.class)
public class LocalUserService implements UserService {

@Override
public String userName() {
return "local user";
}
}

复制@AutoService(UserService.class)
public class RemoteUserService implements UserService {

  @Override
  public String userName() {
    return "remote user";
  }
}

调用

复制public class Client {
  public static void main(String[] args) {
    ServiceLoader<UserService> serviceLoader = ServiceLoader.load(UserService.class);
    for (UserService userService : serviceLoader) {
      System.out.println(userService.userName());
    }
  }
}

输出结果为

复制local user
remote user

从编译之后的目录里可以看到已经生成了对应的配置文件

实现

@SupportedOptions({"debug", "verify"})
public class AutoServiceProcessor extends AbstractProcessor {
@Override
// 处理
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
try {
return processImpl(annotations, roundEnv);
} catch (Exception e) {
// We don't allow exceptions of any kind to propagate to the compiler
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
fatalError(writer.toString());
return true;
}
}
// 真正处理
private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 注解已经处理完毕了,创建配置文件
if (roundEnv.processingOver()) {
generateConfigFiles();
} else {
// 处理注解
processAnnotations(annotations, roundEnv);
}
return true;
}
// 处理注解
private void processAnnotations(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv)
{

Set&lt;? <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Element</span>&gt; elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
<span class="hljs-keyword">for</span> (Element e : elements) {
  <span class="hljs-type">TypeElement</span> <span class="hljs-variable">providerImplementer</span> <span class="hljs-operator">=</span> (TypeElement) e;
  <span class="hljs-type">AnnotationMirror</span> <span class="hljs-variable">annotationMirror</span> <span class="hljs-operator">=</span> getAnnotationMirror(e, AutoService.class).get();
  Set&lt;DeclaredType&gt; providerInterfaces = getValueFieldOfClasses(annotationMirror);
  <span class="hljs-keyword">if</span> (providerInterfaces.isEmpty()) {
    <span class="hljs-keyword">continue</span>;
  }
  <span class="hljs-keyword">for</span> (DeclaredType providerInterface : providerInterfaces) {
    <span class="hljs-type">TypeElement</span> <span class="hljs-variable">providerType</span> <span class="hljs-operator">=</span> MoreTypes.asTypeElement(providerInterface);
    <span class="hljs-keyword">if</span> (checkImplementer(providerImplementer, providerType)) {
      providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
    } <span class="hljs-keyword">else</span> {
    }
  }
}

}

// 创建配置文件
private void generateConfigFiles() {
Filer filer = processingEnv.getFiler();
for (String providerInterface : providers.keySet()) {
String resourceFile = "META-INF/services/" + providerInterface;
try {
SortedSet<String> allServices = Sets.newTreeSet();
try {
FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
resourceFile);
Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
allServices.addAll(oldServices);
} catch (IOException e) {
}
Set<String> newServices = new HashSet<String>(providers.get(providerInterface));
if (allServices.containsAll(newServices)) {
return;
}
allServices.addAll(newServices);
FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
resourceFile);
OutputStream out = fileObject.openOutputStream();
ServicesFiles.writeServiceFile(allServices, out);
out.close();
} catch (IOException e) {
fatalError("Unable to create" + resourceFile + "," + e);
return;
}
}
}

}

AutoServoce 工具和 Lombok 工具是类似的实现原理,通过 java 提供的注解处理器机制,在编译期帮助我们创建一些文件或修改文件。