通過(guò)自定義注解實(shí)現(xiàn)工廠模式

本次介紹自定義編譯時(shí)注解,編譯時(shí)注解,不會(huì)在影響應(yīng)用的打包,沒(méi)有性能問(wèn)題。如果是運(yùn)行時(shí)注解,通過(guò)反射調(diào)用,就會(huì)有性能損耗。

工程結(jié)構(gòu)介紹:

  1. annotationstart:注解接口類定義,需要打包到app模塊中;
  2. annotationcompare:注解掃描工具,不需要打包到app模塊中,但是需要應(yīng)用。
    所以,這兩個(gè)模塊需要分開(kāi);并且需要使用java library創(chuàng)建。
    build.gradle參考:
plugins {
    id 'java-library'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

注解定義

(annotationstart內(nèi))

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface ServiceId {
    String value();
}

定義一個(gè)服務(wù)id,根據(jù)服務(wù)id,創(chuàng)建不同的服務(wù)。

注解掃描類

依賴:

  • javapoet:通過(guò)javapoet,可以使用java面向?qū)ο缶幊躺蒵ava文件,否則只能手動(dòng)拼接字符串,形成java文件,非常麻煩;
  • auto-service:注解自動(dòng)注冊(cè),幫我們自動(dòng)注冊(cè)注解,編譯器才會(huì)開(kāi)始掃描注解,否則不會(huì)生成文件。(如果不使用,可以自己新建javax.annotation.processing.Processor文件,添加注解。具體步驟可以參考原理)。
    implementation project(':annotationstart')
    implementation 'com.squareup:javapoet:1.9.0'
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

需要集成注解接口類,auto-service需要集成,并且打包方式也需要集成。

生成代碼:

@AutoService(Processor.class)
public class ServiceFactoryProcessor extends AbstractProcessor {
    private Types mTypeUtils;
    private Elements mElementUtils;
    private Filer mFiler;
    private Messager mMessager;

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        //初始化我們需要的基礎(chǔ)工具
        mTypeUtils = processingEnv.getTypeUtils();
        mElementUtils = processingEnv.getElementUtils();
        mFiler = processingEnv.getFiler();
        mMessager = processingEnv.getMessager();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> set = new HashSet<>();
        set.add(ServiceId.class.getCanonicalName());
        return set;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, "start");

        // 打印注解
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ServiceId.class);
        for (Element element : elements) {
            String name = element.getSimpleName().toString();
            String value = element.getAnnotation(ServiceId.class).value();
            messager.printMessage(Diagnostic.Kind.NOTE, name + " --> " + value);
            analysisAnnotated(element);
        }
        return true;
    }

    private void analysisAnnotated(Element classElement) {
        TypeElement typeElement = (TypeElement) classElement;
        ClassName appClassName = ClassName.get(mElementUtils.getPackageOf(classElement).asType().toString(), typeElement.getQualifiedName().toString());
        ClassName superClassName = ClassName.get("com.example.annotatetest.test", "IFruit");

        MethodSpec getServiceMethod = MethodSpec.methodBuilder("getService")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addParameter(String.class, "serviceId")
                .returns(superClassName)
                .addStatement("if(serviceId ==\"" + classElement.getAnnotation(ServiceId.class).value() + "\")"
                        + "return new $T()", appClassName)
                .addStatement("\nreturn null")
                .build();

        TypeSpec ServiceFactoryType = TypeSpec.classBuilder("ServiceFactory")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(getServiceMethod)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.annotatetest.test", ServiceFactoryType)
                .build();

        try {
            javaFile.writeTo(mFiler);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用注解

app集成注解接口類和注解掃描類

    implementation project(':annotationstart')
    annotationProcessor project(':annotationcompare')

需要實(shí)現(xiàn)工廠的接口基類:

public interface IFruit {
    void produce();
}

實(shí)現(xiàn)類:

@ServiceId("Apple")
public class Apple implements IFruit {
    @Override
    public void produce() {
        Log.e("AnnotationDemo", "生成蘋果");
    }
}

這樣,就會(huì)開(kāi)始掃描注解,生成工廠類。解決新建一個(gè)服務(wù),需要修改多個(gè)地方的問(wèn)題。

生成類:

import java.lang.String;

public class ServiceFactory {
  public static IFruit getService(String serviceId) {
    if(serviceId =="Apple")return new Apple();
        return null;
  }
}

過(guò)程記錄:

  1. 注解類不生成:沒(méi)有注冊(cè)注解,或者在kotlin中使用注解,由于兼容性問(wèn)題導(dǎo)致掃描失敗。
  2. 調(diào)用工廠類失敗:在kotlin,直接調(diào)用工廠類會(huì)打包失敗,雖然編譯器能識(shí)別,但是打包的時(shí)候會(huì)先編譯kotlin類,這個(gè)時(shí)候注解還沒(méi)掃描。導(dǎo)致打包失敗。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容