1. 程式人生 > >Java編譯時註解應用-生成格式化原始檔

Java編譯時註解應用-生成格式化原始檔

引言

有許多開源框架在編譯時通過註解資訊生成新的原始檔,已到達簡化樣板程式碼的書寫,比如說典型的Builder模式,或者實現框架的功能的橋接程式碼。因為我使用編譯時註解只是想要簡化樣板程式碼,下面我就以Builder模式作為示例。

Builder模式

Builder模式主要是解決建立物件過程中的必須引數和非必須引數的的傳入,以及以及傳入過程中導致物件的中間狀態暴露在外面。下面是簡單Builder模式的實現:

#User.java
package Model.builder;
public class User{
    //定義為final 的屬性必需在建構函式中初始化
    private
final String id; //必需 private final String sex; //必需 private final String name; //必需 private String location; //非必需 private String email; //非必需 private User(String id,String sex,String name){ this.id= id ; this.name =name ; this.sex = sex; } private User(User origin){ this
.id = origin.id; this.sex = origin.sex; this.name = origin .name; this.location = origin.location; this.email = origin.email; } public String getId() { return id; } public String getSex() { return sex; } public String getName
() { return name; } public String getLocation() { return location; } public String getEmail() { return email; } static class UserBuilder{ private User target; public UserBuilder(String id ,String sex,String name){ target = new User(id, sex, name); } public UserBuilder location(String location){ target.location = location; return this; } public UserBuilder email(String email){ target.email = email; return this; } public User build(){ return new User(target); } } }

如果我們要使用上面的builder建立行的User物件的話,像下面這樣呼叫
User user = new User.UserBuilder(“123”,”man”,”sosky”).email(“[email protected]”).location(“xxxxxx”).build()
像這樣鏈式的呼叫寫起來思路很清晰(雖然這裡並沒有很複雜的邏輯,但是如果這裡是多執行緒下網路通訊和UI操作,這樣寫的話邏輯就非常容易理清了,可以參考一下Rxjava的鏈式API),並且避免了User物件中間狀態暴露。

程式碼雖然好用,但是除了規定那些是必需的成員變數,那些是非必需的成員變數,其他的程式碼完全就是重複的樣板程式碼。因此我們就可以採用編譯時註解幫我們處理這些樣板程式碼,只需要我們規定那些是必需的成員變數就行了;

註解的宣告如下:

package Annotation;
//標記builder屬性
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface toBuilder {
    boolean essential();
}
package Annotation;
//標記builder類
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 BuilderClass {

}

我們像這樣定義了一個註解,其中essential表示是否是必需的成員變數,如果是必需的話我們就需要在新的原始檔中定義為final的變數,並在建構函式中初始化它;

註解直譯器宣告:

package Annotation;


import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Set;


/**
 * toBuilder註解解析器
 */
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"Annotation.toBuilder","Annotation.BuilderClass"})
public class toBuilderProcess extends AbstractProcessor{
    private Filer filer;
    private String className;

    //essential為真的屬性名
    private List<String> fields_t;
    //essential為假的屬性名
    private List<String> fields_f;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //標記的類
        Set<? extends Element> classAnnotated = roundEnv.getElementsAnnotatedWith(BuilderClass.class);
        for (Element classelement :classAnnotated){
            className = classelement.getSimpleName().toString();
        }
        //標記的成員變數
        Set<? extends Element> fieldAnnotated = roundEnv.getElementsAnnotatedWith(toBuilder.class);
        for (Element field: fieldAnnotated){
            if(field.getAnnotation(toBuilder.class).essential()) {
                fields_t.add(field.getSimpleName().toString());
            }else{
                fields_f.add(field.getSimpleName().toString());
            }
        }
        //建立原始檔
        createFile();

        return true;
    }


    private void createFile() {

        StringBuilder cls = new StringBuilder();
        cls.append("package Annotation;\n\npublic class ")
                .append(className)
             。。。。。。。
             原始檔內容,根據之前註解資訊拼接字串;
        try {
            JavaFileObject sourceFile = filer.createSourceFile("Annotation." + className);
            Writer writer = sourceFile.openWriter();
            writer.write(cls.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

只有我們就可以通過javac使用編譯後的註解解析器了:
javac -encoding UTF-8 -cp {註解器和註解的包路徑,或者class路徑} -processor {包名.註解器名} -d out\production -s src\ src\Annotation*.java
在這裡注意一下 javac -s標誌的含義,是指定原始檔位置,而我們在註解器中JavaFileObject sourceFile = filer.createSourceFile(“Annotation.” + className); sourceFile物件的Writer的輸出位置就是其指定的路徑,如果沒有- s標誌的話,預設會生成的原始檔會生成到-d 指定的路徑下。我寫的時候遇到了原始檔生成位置不對的坑,就是這個原因。

結語

當然現在有很多IDE外掛能幫我們自動生成Builder程式碼,這只是一個例子,有很多其他樣板程式碼,或者因為小小的不同,需要重複寫的業務邏輯程式碼,都可以用這個方法簡化。更重要的假如我們的框架能夠通過不同註解配置其功能,這樣的高度自定義的框架使用編譯時註解是最為合適的了。

相關推薦

Java編譯註解應用-生成格式化原始檔

引言 有許多開源框架在編譯時通過註解資訊生成新的原始檔,已到達簡化樣板程式碼的書寫,比如說典型的Builder模式,或者實現框架的功能的橋接程式碼。因為我使用編譯時註解只是想要簡化樣板程式碼,下面我就以Builder模式作為示例。 Builder模式

Java編譯註解自動生成程式碼

在開始之前,我們首先申明一個非常重要的問題:我們並不討論那些在執行時(Runtime)通過反射機制執行處理的註解,而是討論在編譯時(Compile time)處理的註解。註解處理器是一個在javac中的,用來編譯時掃描和處理的註解的工具。可以為特定的註解,註冊自己的註解處

關於java編譯註解你需要知道的二三事。解除你的顧慮!

做Android開發。大家肯定會關心你的app的效能問題。不知道從何時開始。網上有流傳一句。不要使用註解。用註解會影響效能。這不能說錯。但是也不能說對。這裡普及一下關於註解的一些你需要知道的知識 網上常說的註解。基本是執行時註解。而所說的註解會影響效能。則

利用編譯註解生成Java原始碼

我們在編寫註解的時候,需要指定@Retention,有三個可選值,表示註解會被保留到那個階段。 RetentionPolicy.SOURCE       這種型別的Annotations只在原始碼級別保留,編譯時就會被忽略,因此一般用來為編譯器提供額外資訊,以便於檢測錯誤,

java註解編譯註解RetentionPolicy.CLASS 基本用法

1 前言 我們知道,在日常開發中我們常用的兩種註解是執行時註解和編譯時註解,執行時註解是通過反射來實現註解處理器的,對效能稍微有一點損耗,而編譯時註解是在程式編譯期間生成相應的代理類,替我們完成某些功能。今天我們來講解一下編譯時註解以及寫一個小例子,以便加深對編譯時註解的理解。

通過編譯註解生成程式碼實現自己的ButterKnife

背景概述 註解的處理除了可以在執行時通過反射機制處理外,還可以在編譯期進行處理。 Java5中提供了apt工具來進行編譯期的註解處理。apt是命令列工具,與之配套的是一套描述“程式在編譯時刻的靜態結構”的API:Mirror API(com.sun.mirr

編譯註解(三)Arouter原始碼講解

專案中我們有時需要跨模組startActivity,但是這樣需要配置menifest,不方便。這時就需要阿里的一個路由框架Arouter Arouter的使用就不再多說了。這篇文章主要講解他的原始碼 1.初始化 ARouter.init( this ); public sta

編譯註解(一)AbstractProcessor實戰

Java中的註解(Annotation)是一個很神奇的東西,特別現在有很多Android庫都是使用註解的方式來實現的。 我們並不討論那些在執行時(Runtime)通過反射機制執行處理的註解,而是討論在編譯時(Compile time)處理的註解。下面便入手學習下

Android 如何編寫基於編譯註解的專案

                     一、概述在Android應用開發中,我們常常為了提升開發效率會選擇使用一些基於註解的框架,但是由於反射造成一定執行效率的損耗,所以我們會更青睞於編譯時註解的框架,例如:butterknife免去我們編寫View的初始化以及事件的注入的程式碼。fragmentargs輕

編譯註解(二)JavaPoet的使用

上一篇文章提到AbstractProcessor中生成java類,可以使用JavaPoet開源庫進行編寫。但是有個問題,addModifier提示無法找到Modifier,其實只要把 compile project(’:libprocess’) 改成 annot

Java編譯一些列錯誤

1、 Description Resource Path Location Type Java compiler level does not match the version of the installed Java project facet. 

Java 編譯多型和執行多型

        根據何時確定執行多型方法中的哪一個,多型分為兩種情況:編譯時多型和執行時多型。如果在編譯時能夠確定執行多型方法 中的哪一個,稱為編譯時多型,否則稱為執行時多型。 一、編譯時多型 方法

使用編譯註解簡單實現類似 ButterKnife 的效果

讀完本文你將瞭解: 什麼是編譯時註解 上篇文章 什麼是註解以及執行時註解的使用 中我們介紹了註解的幾種使用場景,這裡回顧一下: 編譯前提示資訊:註解可以被編譯器用來發現錯誤,或者清除不必要的警告; 編譯時生成程式碼:一些處理器可以在編譯時根據註

kotlin編寫編譯註解

1.定義註解 As裡新建一個Java Library module,必須是Java Library Module 此處命名為route-api 該module存放一些與純java類相關的檔案 1. 定義一個註解 Route /** * @param name

自定義註解編譯註解(RetentionPolicy.CLASS)(二)——JavaPoet

在使用編譯時註解時,需要在編譯期間對註解進行處理,在這裡我們沒辦法影響程式的執行邏輯,但我們可以進行一些需處理,比如生成一些功能性程式碼來輔助程式的開發,最常見的是生成.java 原始檔,並在程式中可以呼叫到生成的檔案。這樣我們就可以用註解來幫助我們處理一些固定邏輯的重複性

[java]初學者java編譯錯誤小總結

初學者常遇Java編譯時錯誤 編譯錯誤 ErrorMessage 錯誤: 非法的型別開始 illegal start of type 錯誤: 需要’;’ ‘;’ expected 錯誤: 方法宣告無效;需要返回

java 自定義註解應用例項

本例子旨在使用自定義註解為實體打上標記,為自動生成 sql 提供依據,模擬 hibernate 的註解,至於註解的原理自己搜吧1.定義 Table 註解package test; import java.lang.annotation.Documented; import

Android 如何編寫基於編譯註解的專案---轉載張鴻洋博文

一、概述 在Android應用開發中,我們常常為了提升開發效率會選擇使用一些基於註解的框架,但是由於反射造成一定執行效率的損耗,所以我們會更青睞於編譯時註解的框架,例如: butterknife免去我們編寫View的初始化以及事件的注入的程式碼。fragmentargs輕鬆的為fragment新增引數資

java 編譯出現'\ufeff' 編碼錯誤

使用QtCreator 編寫Android 上的java程式時,編譯總是出現'\ufeff' 錯誤。解決辦法:取消該java檔案的UTF-8 BOM,使用無BOM編碼。在QtCreator 工具-> 選項 -> 文字編輯器 -> 行為  中,將UTF-8 B

如何除錯編譯註解處理器AnnotationProcessor

本來的話是想跟大家分享如何製作自己的編譯時註解處理器的,後來搜尋了一下發現網上有不少這方面的文章,寫得都很全面很優秀,所以就不獻醜了。如果大家還不知道怎麼寫自己的編譯時註解處理器,可以看下這位大神寫的文章學習下:http://blog.csdn.net/lmj6