Java編譯時註解自動生成程式碼
在開始之前,我們首先申明一個非常重要的問題:我們並不討論那些在執行時(Runtime)通過反射機制執行處理的註解,而是討論在編譯時(Compile time)處理的註解。註解處理器是一個在javac中的,用來編譯時掃描和處理的註解的工具。可以為特定的註解,註冊自己的註解處理器。
一個註解的註解處理器,以Java程式碼(或者編譯過的位元組碼)作為輸入,生成檔案(通常是.java檔案)作為輸出。可以生成Java程式碼,這些生成的Java程式碼是在生成的.java檔案中,所以不能修改已經存在的Java類,例如向已有的類中新增方法。這些生成的Java檔案,會同其他普通的手動編寫的Java原始碼一樣被javac編譯。
虛處理器AbstractProcessor
我們首先看一下處理器的API。每一個處理器都是繼承於AbstractProcessor,如下所示:
public class MyProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment env){ } @Override public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { } @Override public Set<String> getSupportedAnnotationTypes() { } @Override public SourceVersion getSupportedSourceVersion() { } }
- init(ProcessingEnvironment env): 每一個註解處理器類都必須有一個空的建構函式。然而,這裡有一個特殊的init()方法,它會被註解處理工具呼叫,並輸入ProcessingEnviroment引數。ProcessingEnviroment提供很多有用的工具類Elements,Types和Filer。
- process(Set<? extends TypeElement> annotations, RoundEnvironment env): 這相當於每個處理器的主函式main()。 在這裡寫掃描、評估和處理註解的程式碼,以及生成Java檔案。輸入引數RoundEnviroment,可以讓查詢出包含特定註解的被註解元素。
- getSupportedAnnotationTypes(): 這裡必須指定,這個註解處理器是註冊給哪個註解的。注意,它的返回值是一個字串的集合,包含本處理器想要處理的註解型別的合法全稱。換句話說,在這裡定義你的註解處理器註冊到哪些註解上。
- getSupportedSourceVersion(): 用來指定你使用的Java版本。通常這裡返回SourceVersion.latestSupported()。然而,如果有足夠的理由只支援Java 6的話,也可以返回SourceVersion.RELEASE_6。推薦使用前者。
舉一個簡單例子
自動生成一個bean的結構檔案
把
public class Student {
public String stu_name;
public String stu_id;
public int stu_age;
}
轉換為
{class:"com.robert.processor.Student",
fields:
{
stu_name:"java.lang.String",
stu_id:"java.lang.String",
stu_age:"java.lang.Integer"
}
}
首先宣告註解
package com.robert.processor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.CLASS)
public @interface Serialize {
}
將註解加到Student類上@Serialize
public class Student
定義自己的解析器
package com.robert.processor;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
public class MyProcessor extends AbstractProcessor {
// 元素操作的輔助類
Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 獲得被該註解宣告的元素
Set<? extends Element> elememts = roundEnv.getElementsAnnotatedWith(Serialize.class);
TypeElement classElement = null;// 宣告類元素
List<VariableElement> fields = null;// 宣告一個存放成員變數的列表
// 存放二者
Map<String, List<VariableElement>> maps = new HashMap<String, List<VariableElement>>();
// 遍歷
for (Element ele : elememts) {
// 判斷該元素是否為類
if (ele.getKind() == ElementKind.CLASS) {
classElement = (TypeElement) ele;
maps.put(classElement.getQualifiedName().toString(), fields = new ArrayList<VariableElement>());
} else if (ele.getKind() == ElementKind.FIELD) // 判斷該元素是否為成員變數
{
VariableElement varELe = (VariableElement) ele;
// 獲取該元素封裝型別
TypeElement enclosingElement = (TypeElement) varELe.getEnclosingElement();
// 拿到key
String key = enclosingElement.getQualifiedName().toString();
fields = maps.get(key);
if (fields == null) {
maps.put(key, fields = new ArrayList<VariableElement>());
}
fields.add(varELe);
}
}
for (String key : maps.keySet()) {
if (maps.get(key).size() == 0) {
TypeElement typeElement = elementUtils.getTypeElement(key);
List<? extends Element> allMembers = elementUtils.getAllMembers(typeElement);
if (allMembers.size() > 0) {
maps.get(key).addAll(ElementFilter.fieldsIn(allMembers));
}
}
}
generateFile(maps);
return true;
}
private void generateFile(Map<String, List<VariableElement>> maps) {
File dir = new File(MyProcessor.class.getResource("/").getPath());
if (!dir.exists())
dir.mkdirs();
// 遍歷map
for (String key : maps.keySet()) {
// 建立檔案
File file = new File(dir, key.replaceAll("\\.", "_") + ".txt");
try {
/**
* 編寫檔案內容
*/
FileWriter fw = new FileWriter(file);
fw.append("{").append("class:").append("\"" + key + "\"").append(",\n ");
fw.append("fields:\n {\n");
List<VariableElement> fields = maps.get(key);
for (int i = 0; i < fields.size(); i++) {
VariableElement field = fields.get(i);
fw.append(" ").append(field.getSimpleName()).append(":")
.append("\"" + field.asType().toString() + "\"");
if (i < fields.size() - 1) {
fw.append(",");
fw.append("\n");
}
}
fw.append("\n }\n");
fw.append("}");
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = super.getSupportedAnnotationTypes();
if (set == null) {
set = new HashSet<>();
}
set.add("com.robert.processor.Serialize");
return set;
}
}
我們經常使用的ButterKnife這個框架就很好的使用了AbstractProcessor
Butter Knife 是 Android 檢視欄位和方法繫結,使用註解處理來生成樣板程式碼。後面做詳細說明。
歡迎掃描二維碼,關注個人公眾號
相關推薦
Java編譯時註解自動生成程式碼
在開始之前,我們首先申明一個非常重要的問題:我們並不討論那些在執行時(Runtime)通過反射機制執行處理的註解,而是討論在編譯時(Compile time)處理的註解。註解處理器是一個在javac中的,用來編譯時掃描和處理的註解的工具。可以為特定的註解,註冊自己的註解處
Java編譯時註解應用-生成格式化原始檔
引言 有許多開源框架在編譯時通過註解資訊生成新的原始檔,已到達簡化樣板程式碼的書寫,比如說典型的Builder模式,或者實現框架的功能的橋接程式碼。因為我使用編譯時註解只是想要簡化樣板程式碼,下面我就以Builder模式作為示例。 Builder模式
Java語言使用註解處理器生成程式碼 —— 第一部分:註解型別
原文作者:deors 原文地址:https://deors.wordpress.com/2011/09/26/annotation-types/ 譯文作者:Jianan - [email protected] 版本資訊:本文基於
關於java編譯時註解你需要知道的二三事。解除你的顧慮!
做Android開發。大家肯定會關心你的app的效能問題。不知道從何時開始。網上有流傳一句。不要使用註解。用註解會影響效能。這不能說錯。但是也不能說對。這裡普及一下關於註解的一些你需要知道的知識 網上常說的註解。基本是執行時註解。而所說的註解會影響效能。則
kotlin結合dagger2使用為什麼在編譯時無法自動生成DaggerxxxComponent類
算是一個小坑,卡了我大半天的時間解決方法很簡單,只要將gradle裡面依賴的apt改成kapt就行了,比如dagger2裡面的dagger-compiler,databinding裡面的compiler
通過編譯時註解生成程式碼實現自己的ButterKnife
背景概述 註解的處理除了可以在執行時通過反射機制處理外,還可以在編譯期進行處理。 Java5中提供了apt工具來進行編譯期的註解處理。apt是命令列工具,與之配套的是一套描述“程式在編譯時刻的靜態結構”的API:Mirror API(com.sun.mirr
利用編譯時註解生成Java原始碼
我們在編寫註解的時候,需要指定@Retention,有三個可選值,表示註解會被保留到那個階段。 RetentionPolicy.SOURCE 這種型別的Annotations只在原始碼級別保留,編譯時就會被忽略,因此一般用來為編譯器提供額外資訊,以便於檢測錯誤,
java註解之編譯時註解RetentionPolicy.CLASS 基本用法
1 前言 我們知道,在日常開發中我們常用的兩種註解是執行時註解和編譯時註解,執行時註解是通過反射來實現註解處理器的,對效能稍微有一點損耗,而編譯時註解是在程式編譯期間生成相應的代理類,替我們完成某些功能。今天我們來講解一下編譯時註解以及寫一個小例子,以便加深對編譯時註解的理解。
mybatis generator自動生成程式碼時 只生成了insert 而沒有其他的
mybatis框架提供了非常好用的逆向工程外掛,但是在使用過程中會有很多問題。 我在使用中就遇到了只生成insert和insertSeletive方法,而不生成其他根據primary key查詢更新刪除的方法。 解決方案: 1.檢查資料庫中的表是否有主鍵,如果沒有主鍵是不會生成類似selectByPri
VS:編譯dll時,自動生成版本
步驟 1. 在解決方案中--》現有項xxx.rc檔案--》雙擊xxx.rc檔案開啟資源檢視--》選中xxx.rc有右鍵“新增資源”--》選擇Version
SSM+Maven整合時在Eclipse中使用Mybatis逆向工程自動生成程式碼
場景 MybatisGenerator 官方文件 http://www.mybatis.org/generator/configreference/xmlconfig.html 實現 專案搭建好完整的包,包括bean、dao、service、test、utils、mapper
Mybatis generator 自動生成程式碼時 ,insert返回主鍵值
mvn mybatis-generator:generate 如果要讓generator自動新增該功能,可以如下配置: <!-- tableName:用於自動生成程式碼的資料庫表;domainObjectName:對應於資料庫表的javaBean類名;不需要生成Ex
使用java語言中的註解生成器生成程式碼
Code Generation using Annotation Processors in the Java language – part 1: Annotation Types 註解型別 這篇帖子我會開始關於使用java語言中註解處理器來程式碼生
Mybatis generator 自動生成程式碼時 ,insert如何返回主鍵值
mvn mybatis-generator:generate 如果要讓generator自動新增該功能,可以如下配置: <!-- tableName:用於自動生成程式碼的資料庫表;domainObjectName:對應於資料庫表的javaBean類名;不需要生成Ex
mybatis使用generator自動生成程式碼時的型別轉換
使用mybatis的generator自動生成程式碼,但是oracle資料庫中number(6,2)總是自動轉成BigDecimal,我想要轉成的是float型別 這樣就寫了一個型別轉換器,需要繼承JavaTypeResolver介面 然後在mybaties配置檔案gene
android apt編譯時期自動生成程式碼
最近新了一個架構,之前用dagger2時候,每當新增新activity還要修改或者新建component來完成dagger的注入。用了apt以後,在activity上標註一個註解就可以了。 本文章用最簡單的方法最直白的話 來搭建一個簡單的apt編譯時期生成程式
mybatis generator自動生成程式碼時將blob屬性變成String型別,當出現亂碼時的處理方法
1.blob屬性變成String型別,需要在配置檔案中的table元素中增加columnOverride元素,詳情如下例子: <table tableName="paas_conf_metadata"> <generatedKey column="ID"
eclipse、myeclipse寫類時,自動生成註釋
圖片 version inf pre nbsp tags bubuko 內容 types 在類的上邊/**+enter自動生成註釋。 設置方法:Window--Prefences--Java--Code Style--Code Templates--Comments-
pycharm中每次創建py文件時就自動生成代碼頭,以及出現SyntaxError:Non-ASCII 。。。問題
char ror utf 生成 CI 出現 每次 class font 我們在pycharm中執行py文件的時候,可能會出現以下錯誤 這是因為你沒有制定編碼格式,這時候你需要在文件最開始制定編碼格式,代碼如下 #!/user/bin/env python
mybatis-generator-maven-plugin外掛自動生成程式碼的配置方法
1. 第一步,在pom檔案中引入如下外掛 //專案示例程式碼: <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-g