美文网首页
AOP思想实践之APT

AOP思想实践之APT

作者: 编程的猫 | 来源:发表于2021-08-24 22:43 被阅读0次

    AOP优势:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    AOP分类:
    从织入的时机的角度看,可以分为源码阶段、class阶段、dex阶段、运行时织入。
    对于前三项源码阶段、class阶段、dex织入,由于他们都发生在class加载到虚拟机前,我们统称为静态织入,
    而在运行阶段发生的改动,我们统称为动态织入。

    织入时机 技术框架
    静态织入 APT,AspectJ、ASM、Javassit
    动态织入 java动态代理,cglib、Javassit

    很多开源框架都是用了APT动态生成代码技术,例如:ARouter、ButterKnife
    这里APT实践以手写一个简单的findViewById为例子。APT需要注解和反射的知识,请自行学习。
    在Android工程中除了app module外,新建两个java library
    java library一,例如名称annotationsLib,其build.gradle如下:

    plugins {
        id 'java-library'
    }
    
    // 控制台中文设置UTF-8
    tasks.withType(JavaCompile){
        options.encoding = "UTF-8"
    }
    
    java {
        sourceCompatibility = JavaVersion.VERSION_1_7
        targetCompatibility = JavaVersion.VERSION_1_7
    }
    

    新建以下注解:

    // 编译时注解
    @Retention(RetentionPolicy.CLASS)
    // 作用在类上
    @Target(ElementType.TYPE)
    public @interface ViewPath {
    
    }
    
    // 运行时注解
    @Retention(RetentionPolicy.RUNTIME)
    // 作用在变量上
    @Target(ElementType.FIELD)
    public @interface ViewId {
        // viewId
        int value() default 0;
    }
    

    新建java Library二,名称:compilerLib,其build.gradle如下:

    plugins {
        id 'java-library'
    }
    
    // 控制台中文设置UTF-8
    tasks.withType(JavaCompile){
        options.encoding = "UTF-8"
    }
    
    java {
        sourceCompatibility = JavaVersion.VERSION_1_7
        targetCompatibility = JavaVersion.VERSION_1_7
    }
    
    dependencies{
        // 编译时期进行注解处理
        annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
        compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
        // 生成代码
        implementation 'com.squareup:javapoet:1.11.1'
        implementation project(':annotationsLib')
    }
    

    上边的auto-service依赖是自动注册注解,javapoet 依赖是方便使用api构建生成java代码文件。APT的核心代码如下:(注意:process方法会调用多次,多次生成相同的文件会抛出异常,只有第一次Set<? extends TypeElement> annotations才会有值,根据annotations中的注解类型判断我们当前要使用的目标注解)

    @AutoService(Processor.class)
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    @SupportedAnnotationTypes({Constance.VIEW_PATH, Constance.HELLO_POET})
    public class ViewIdProcessor extends AbstractProcessor {
    
        // 获取类的工具对象
        private Elements elementUtils;
        // 获取类型的工具对象
        private Types typeUtils;
        private Filer filer;
    
        @Override
        public synchronized void init(ProcessingEnvironment processingEnv) {
            super.init(processingEnv);
            System.out.println("=============【进入了 init 方法】==============");
            elementUtils = processingEnv.getElementUtils();
            typeUtils = processingEnv.getTypeUtils();
            filer = processingEnv.getFiler();
    
        }
    
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            System.out.println("=============【进入了 process 方法】==============");
    
            if (annotations != null) {
                for (TypeElement typeElement : annotations) {
                    String qualifiedName = typeElement.getQualifiedName().toString();
                    String simpleNam = typeElement.getSimpleName().toString();
                    System.out.println("== qualifiedName:" + qualifiedName + " -> simpleNam:" + simpleNam);
                    if (qualifiedName.equals(Constance.HELLO_POET)) {
                        // 生成HelloPoet文件
                        testHello();
                    }else if (qualifiedName.equals(Constance.VIEW_PATH)){
                        // 生成ViewPath文件
                        generateViewPath(annotations, roundEnv);
                    }
                }
            }
    
            return true;
        }
    
        /**
         * 生成ViewPath注解标识的类
         */
        public void generateViewPath(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            // 获取指定注解类型修饰的所有类   这里指定的注解类型:ViewPath.class
            Set<? extends Element> elementsAnnotatedWithSet = roundEnv.getElementsAnnotatedWith(ViewPath.class);
            for (Element element : elementsAnnotatedWithSet) {
    
                // 判断element类型是否为class
                TypeElement typeElement = (TypeElement) element;
    
                // 获取当前类中的所有成员
                List<? extends Element> allMembers = elementUtils.getAllMembers(typeElement);
    
                // 生成方法
                MethodSpec.Builder builderMethod = MethodSpec.methodBuilder("bindView")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .returns(TypeName.VOID)
                        .addParameter(ClassName.get(typeElement.asType()), "activity");
    
                for (Element member : allMembers) {
                    // 获取类中成员上的所有注解
    //                List<? extends AnnotationMirror> allAnnotationMirrorsWithMember = elementUtils.getAllAnnotationMirrors(member);
    //                if (allAnnotationMirrorsWithMember!=null){
    //
    //                }
                    // 获取成员上指定类型的注解,被该注解修饰的需要赋值
                    ViewId annotationViewId = member.getAnnotation(ViewId.class);
                    if (annotationViewId == null) continue;
    
                    String formatStatement = String.format("activity.%s = (%s) activity.getWindow().getDecorView().findViewById(%s)",
                            member.getSimpleName(), ClassName.get(member.asType()).toString(), annotationViewId.value());
                    builderMethod.addStatement(formatStatement);
                }
                // 构造类
                TypeSpec buildClazz = TypeSpec.classBuilder(typeElement.getSimpleName() + "_BindView")
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                        .addMethod(builderMethod.build())
                        .build();
    
                // 构造java文件内容  指定java文件生成的路径
                JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), buildClazz).build();
                try {
                    javaFile.writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 获取包名
         * @param typeElement typeElement
         * @return 返回包名
         */
        private String getPackageName(TypeElement typeElement){
            // Qualified 获取全路径名称
            return elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
        }
    
    }
    

    代码中的注释已经写的很详细。点击rebuild project后就会在对应的包下生成java文件,例如本文中的路径:com/pluginchildapp/aopTest/AopActivity_BindView.java

    app module依赖annotationsLib和compilerLib,build.gradle如下:

    dependencies {
        implementation 'androidx.appcompat:appcompat:1.2.0'
        implementation 'com.google.android.material:material:1.3.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
        implementation 'androidx.legacy:legacy-support-v4:1.0.0'
       
        testImplementation 'junit:junit:4.+'
        androidTestImplementation 'androidx.test.ext:junit:1.1.2'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    
        // 依赖注解和注解处理器
        implementation project(path: ':annotationsLib')
        annotationProcessor project(path: ':compilerLib')
        // 注解api调用库
        implementation project(path: ':aop-coreLib')
    }
    

    另外再新建一个Android library,例如名字叫:aop-coreLib,同样在app module中进行依赖。在aop-coreLib中新建类ViewBinder,这个类的作用是为了给Activity中的成员赋值,看下边的代码就明白了。
    ViewBinder的代码

    public class ViewBinder {
    
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static void inject(Object target) {
            if (target == null) throw new NullPointerException("object is null");
            String simpleName = target.getClass().getSimpleName();
            String packageName = target.getClass().getPackage().getName();
            String className = packageName + "." + simpleName + "_BindView";
    
            try {
                Class<?> aClass = Class.forName(className);
                Method bindView = aClass.getMethod("bindView", target.getClass());
                bindView.invoke(null, target);
            } catch (Exception e) {
                e.printStackTrace();
                Log.e("==TAT", "出错:" + e.getMessage());
            }
        }
    }
    

    在Activity中使用:

    @ViewPath
    public class AopActivity extends AppCompatActivity {
    
    
        @ViewId(value = R.id.tv)
        TextView textView;
    
        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_aop);
    
            ViewBinder.inject(this);
            textView.setText("赋值啦。。。");
    
            HelloPoetClazz.helloPoet("哈哈哈");
        }
    }
    

    AOP思想很重要,掌握它的所有实现方式很重要!!!

    相关文章

      网友评论

          本文标题:AOP思想实践之APT

          本文链接:https://www.haomeiwen.com/subject/jcpjiltx.html