Java注解处理(Annotation Processor):(四) 完结

        接着上节的内容,首先谈一下poet的使用。通过上节可以看出,通过Filer生成代码时,使用了字符串方式的拼接与替换来生成Java源码,十分不利于修改与调整,而且比较容易出错,而poet就是解决这个问题的神器。

        上节的代码修改后如下:

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.dream.test.annotation.Hello")
@SupportedOptions("debug")
@AutoService(Processor.class)
public class HelloProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        for (Element element : roundEnvironment.getElementsAnnotatedWith(Hello.class)) {
            TypeElement typeElem = (TypeElement) element;
            String typeName = typeElem.getQualifiedName().toString();
            Filer filer = processingEnv.getFiler();
            try (Writer sw = filer.createSourceFile(typeName + "Hello").openWriter()) {
                log("Generating " + typeName + "Hello source code");
                int lastIndex = typeName.lastIndexOf('.');
                MethodSpec helloMethod =
                        MethodSpec.methodBuilder("sayHello").addModifiers(Modifier.PUBLIC, Modifier.STATIC).returns(void.class)
                                .addStatement("$T.out.println($S)", System.class, "Hello, " + typeName).build();
                TypeSpec helloType = TypeSpec.classBuilder(typeName.substring(lastIndex + 1) + "Hello").addModifiers(Modifier.PUBLIC)
                        .addMethod(helloMethod).build();
                JavaFile helloFile = JavaFile.builder(typeName.substring(0, lastIndex), helloType).build();
                helloFile.writeTo(sw);
            } catch (IOException e) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
            }
        }
        return true;
    }

    private void log(String msg) {
        if (processingEnv.getOptions().containsKey("debug")) {
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
        }
    }
}

       通过Poet提供的MethodSpec、TypeSpec等进行方法和类的定义,最后输出到文件。使用方法还是比较简单的,具体可以直接参照其Github主页

       到此为止相关的Annotation Processor的相关内容基本介绍完毕了,最后需要了解的是,他具体能够解决开发中的哪些问题。其实无论是ButterKnife还是Dagger2,依赖注入是其主要的应用之一,但是它们是如果通过自动代码生成完成依赖注入的呢?我们已Dagger2为切入点来看看具体Dagger2为我们生成了哪些代码,简单示例如下(如果对于Dagger还不了解,可以先去主页熟悉一下):

public class Car {
    @Inject
    Wheel wheel;

    public Car() {}

    public Wheel getWheel() {
        return wheel;
    }
}

public class Wheel {
    @Inject
    public Wheel() {}
}

@Component
public interface CarMaker {
    void inject(Car car);
}

         主要的类包括待装配车轮的车子(Car),车轮(Wheel)和车轮装配车间(CarMaker),接下来在主程序中进行装配即可:

public class Main {
    public static void main(String[] args) {
        CarMaker carMaker = DaggerCarMaker.create();
        Car car = new Car();
        carMaker.inject(car);
    }
}

         那Dagger是如何利用Annotation Processor实现汽车轮子的生产与装配(注入)的呢,可以关注一下其自动生成的代码:

        从名字就可以看出,主要生成了一个Wheel的工厂类Wheel_Factory用于轮子的生产,较为简单就不赘述了;一个Car_MembersInjector类,用于汽车轮子的装配(注入);一个DaggerCarMaker车轮转配车间实例。

        Car_MembersInjector类的内容如下所示,其装配车轮的主要方法就是injectWheel,实际相当简单,由于生成的Car_MembersInjector与Car在相同包下,而Car的wheel成员变量是无修饰符的,也就是说对于相同包内的类是可见的,为此在组装时直接通过instance.wheel=wheel就能完成所谓的注入过程

public final class Car_MembersInjector implements MembersInjector<Car> {
  private final Provider<Wheel> wheelProvider;

  public Car_MembersInjector(Provider<Wheel> wheelProvider) {
    this.wheelProvider = wheelProvider;
  }

  public static MembersInjector<Car> create(Provider<Wheel> wheelProvider) {
    return new Car_MembersInjector(wheelProvider);
  }

  @Override
  public void injectMembers(Car instance) {
    injectWheel(instance, wheelProvider.get());
  }

  public static void injectWheel(Car instance, Wheel wheel) {
    instance.wheel = wheel;
  }
}

        DaggerCarMaker的代码如下,主要就是实现了inject方法,并调用了Car_MembersInjector的injectWheel方法来进行车轮的装配注入:

public final class DaggerCarMaker implements CarMaker {
  private DaggerCarMaker() {}

  public static Builder builder() {
    return new Builder();
  }

  public static CarMaker create() {
    return new Builder().build();
  }

  @Override
  public void inject(Car car) {
    injectCar(car);
  }

  private Car injectCar(Car instance) {
    Car_MembersInjector.injectWheel(instance, new Wheel());
    return instance;
  }

  public static final class Builder {
    private Builder() {}

    public CarMaker build() {
      return new DaggerCarMaker();
    }
  }
}

        从生成代码中就能看出Dagger2是如何通过注解的代码生成来完成依赖注入框架的,避免了使用反射的机制,从而提高了运行时的效率,简单却相当巧妙。

       为此个人感觉,当你的代码中有相当多的重复或雷同的代码,或者有了较多的依赖反射的功能时,就可以考虑考虑是否可以用Annotation Processor来进行代码简化或注入的工作了。

发布了42 篇原创文章 · 获赞 9 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/jjxojm/article/details/90381699