Java注解处理(Annotation Processor):(一) 从SPI谈起

       最早接触Java Annotation Processor应该还是要数使用ButterKnife、Dagger2了,当时对Java注解的理解和使用还停留在通过运行时的反射机制来进行注解解析(主要还是用来替代恶心的XML配置)。初次接触时确实让人耳目一新,基本上是通过注解处理和代码生成,完成了依赖注入,不仅是在概念上面,而且对于性能资源有限的APP端,让使用依赖注入成为可行的方案;当然他的优点不止于此,Annotation Processor再结合poet这样的代码生成工具,使得现有的各种第三方框架与代码越来越简介,例如lombok等工具都是基于此实现的。

       最近打算好好梳理一下这部分知识,首先就从基础的SPI(Service Provider Interface)谈起。


       SPI直译就是服务提供接口,从名字就可以看出,应该是类似于DI依赖注入的概念,简单来说就是在程序中通过接口声明服务,而具体的服务实现则依赖于框架将其注入,只不过于Spring的DI或IoC不同,SPI并不是通过Spring架构以及注解或XML配置实现依赖注入,而是通过java.util.ServiceLoader进行服务实现的发现与实例化。

       具体示例如下:

// 接口
public interface Api {
    String getName();
}

        定义好对应接口之后,就是对应的实现: 

// 实现A
public class AImpl implements Api {
    @Override
    public String getName() {
        return "A Implementation";
    }
}

        实现A的对应库Jar包中需要在META-INF/services下放入名称为接口Api完整类名的文件,并填入对应实现类名(如Jar包内有多个实现,则用换行进行分隔),如下所示:

Java注解处理(Annotation Processor):(一) 从SPI谈起_第1张图片

         实现B也同样,如下所示:

// 实现B
public class BApi implements Api {
    @Override
    public String getName() {
        return "B Implementation";
    }
}

Java注解处理(Annotation Processor):(一) 从SPI谈起_第2张图片

          具体使用时如下,引入接口和对应实现库(以gradle为例):

dependencies {
    compile project(':interface')
    compile project(':liba')
    compile project(':libb')
}

         而在代码中使用ServiceLoader即可获得Api接口对应的所有SPI实现:

public class Main {
    public static void main(String[] args) {
        ServiceLoader loader = ServiceLoader.load(Api.class);
        for (Api source : loader) {
            System.out.println(source.getName());
        }
    }
}

       整体来说,SPI以及对应的ServiceLoader的实现还是比较容易理解的,那它和我们说的Annotation Processor又有什么联系呢,其实准确来说没有什么关系,SPI准确说还是和反射类似,属于运行时的动态技术,而如前所述Annotation Processor是与Javac编译时相关的处理技术(可能Javac进行注解处理的时候,也使用了SPI技术,但是具体没有去进一步考证,但个人认为应该还是要区分开来理解的)。

       说了这么多,可能就有人要开骂了,既然Annontation Processor和SPI没有关系,你还介绍这干嘛?其实这也是我一开始的疑惑,很多介绍Annotation Processor的文章或者示例中,都会用到Google的Auto Service库,而Auto Service就是一种SPI META-INF/services下文件的一种自动生成工具,这就导致我当时想当然的认为Annotation Processor应该是SPI的一种特殊形式,而深入了解之后发现并非如此。

       在我看来可能刚好Annotation Processor使用了与SPI相同的定义方式,都是通过META-INF/services下的文件来定义对应实现的,而Auto Service刚好用来生成该文件,为此,Annotation Processor在实现时,也就同样使用Auto Service来生成实现描述文件。可能聪明的读者就会问,那Auto Service是怎么实现这种编译时的文件生成的呢?答案就是Annotation Processor(是不是有点绕?)。后续,我将以Google Auto Service源码为切入点,来探讨Annotation Processor的实现!

你可能感兴趣的:(JAVA)