【Java學習筆記(一百二十一)】之 指令碼語言使用,註解的詳細介紹
本文章由公號【開發小鴿】釋出!歡迎關注!!!
老規矩–妹妹鎮樓:
一. 指令碼
(一) 概述
指令碼語言是一種通過在執行時解釋程式文字,從而避免使用通常的編輯/編譯/連結/執行迴圈的語言,指令碼語言的優勢是快速變更,可以修改執行著的程式,但是,指令碼語言缺乏可以使編寫複雜應用受益的特性,因此人們通常將指令碼語言與傳統語言的優勢結合起來。指令碼API使你可以在Java平臺上實現這個目的,直接在Java程式中呼叫指令碼。
(二) 指令碼引擎
1. 概述
指令碼引擎是一個可以執行用某種特定語言編寫的指令碼的類庫,當虛擬機器啟動時,它會發現可用的指令碼引擎,為了列舉這些引擎,需要構造一個ScriptEngineManager,並呼叫getEngineFactories方法,可以向每個引擎工廠詢問他們所支援的引擎名,MIME型別和副檔名,通過這三個引數來獲取引擎。
ScriptEngine engine = manager.getEngineByName(“nashorn”);
2. 指令碼計算與繫結
擁有了引擎,可以直接呼叫指令碼:
Object result = engine.eval(scriptString);
engine eval(“n = 1”);
可以在一個引擎上呼叫多個指令碼,如果一個指令碼定義了變數,函式或者類,那麼大多數引擎都會保留這些定義。
可以直接向引擎中新增新的變數繫結,繫結由名字及其關聯的java物件構成:
engine.put(“k”, 1);
指令碼程式碼從“引擎作用域”中的繫結裡讀取k的定義,還可以新增到全域性作用域中,任何新增到ScriptEngineManager中的繫結對於所有引擎都是可視的。
3. 重定向輸入和輸出
可以通過呼叫指令碼上下文的setReader和setWriter方法來重定向指令碼的標準輸入和輸出。
var writer = new StringWriter();
engine.getContext().setWriter(new PrintWriter(writer, true));
那麼指令碼產生的輸出都會被髮送到writer上。
4. 呼叫指令碼的函式
對於許多指令碼引擎而言,都可以呼叫指令碼語言的函式,而不用對實際的指令碼程式碼進行計算,提供這種功能的指令碼引擎實現了Invocable介面。要呼叫一個函式,需要使用函式名來呼叫invokeFunction方法,函式名後面是函式的引數。
engine.eval(“function greet(how, whom) {return how + “,” + whom }”);
result = ((Invocable) engine).invokeFunction(“greet”, “hello”, “world”);
進一步,可以讓指令碼引擎實現一個Java介面,然後就可以用Java方法呼叫的語法來呼叫指令碼函式,需要為該Java介面中的每個方法都提供一個函式。如下面的介面:
public interface Greeter{
String welcome(String whom);
}
那麼呼叫時,首先獲取給定介面的實現類,該實現用指令碼引擎中的函式實現了介面中的方法。
engine.eval(“function welcome(whom) {return “hello” + whom}”);
Greeter g = ((Invocable ) engine).getInterface(Greeter.class);
result = g.welcome(“world”);
5. 編譯指令碼
某些指令碼引擎出於執行效率的考慮,將指令碼程式碼編譯為某種中間格式,這些引擎實現了Compilable介面,一旦指令碼被編譯,可以直接執行它,當然只有在需要重複執行某個指令碼時,才會編譯指令碼。
二. 註解
(一) 概述
註解是那些插入到原始碼中使用其他工具可以對其進行處理的標籤,這些工具可以在原始碼層次上操作,或者可以處理編譯器在其中放置了註解的類檔案。註解並不會改變程式的編譯方式,Java編譯器對於包含註解和不包含註解的程式碼會生成相同的虛擬機器指令。
(二) 註解的用途
附屬檔案的自動生成,例如部署描述符或者bean資訊類;
測試,日誌,事務語義等程式碼的自動生成;
(三) 註解的使用
Java中,註解被當做一個修飾符倆使用的,它被置於註解項之前,中間沒有分號,每個註解的名稱之前都加上了@符號,如下所示:
public class MyClass{
@Test
public void checkRandom();
}
@Test註解自身不會做任何事,它需要工具的支援,如測試一個類時,Junit4測試工具可能會呼叫所有標識為@Test的方法。
註解也可以定義成包含元素的形式,如:
@Test(timeout=”10000”)
這些元素可以被讀取這些註解的工具去處理,除了方法以外,還可以註解類,成員以及區域性變數,這些註解可以存在於任何可以放置像public這樣的修飾符的地方。每個註解需要通過一個註解介面來定義,這些介面中的方法與註解中的元素相對應,如下所示:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test
{
long timeout() default 0L;
}
@interface宣告建立一個真正的Java介面,處理註解的工具將接受那些實現了註解介面的物件,這類工具可以呼叫timeout方法來獲取某個特定Test註解的timeout元素。
@Target註解和@Retention是元註解,它們註解了Test註解,將Test註解標識為一個只能運用到方法上的註解,並且當類檔案載入到虛擬機器上時,它仍然可以保留下來,具體的在後面討論。
定義了註解物件,這些註解只會存在於原始檔中,編譯器會將它們置於類檔案中,並且虛擬機器會載入這些檔案。我們需要使用一個能夠分析註解的工具來接受這些註解物件,這個工具類將會呼叫一個處理方法,該方法將會枚舉出所有方法的註解物件,如果存在對應的註解物件,就對這個物件進行處理,註解也就真正起了作用。
(四) 註解的語法
所有的註解介面都隱式地擴充套件自java.lang.annotation.Annotaion介面,無需為註解介面提供實現類。如果註解中某個元素的值並未指定,就是用宣告的預設值,預設值並不是和註解儲存在一起的,它們是動態計算得來的。
有兩個特殊的註解可以簡化註解,一個是標記註解,註解中沒有任何元素或者所有元素都使用預設值,如下所示:
@BugReport
另一個是單值註解,如果一個元素具有特殊的名字value,且沒有指定其他元素,則可以忽略這個value的元素名,直接在寫值:
@ActionLiener(“yellow”)
一個項可以有多個註解,且如果註解的作者宣告註解為可重複的,那麼可以重複使用同一個註解。一個註解元素可以是另一個註解,因此可以將註解巢狀使用。
(五) 註解的位置
註解可以出現在很多地方,這些地方分為兩類,一類是宣告註解,一類是型別用法宣告註解。宣告註解放置到其他修飾符的前面,型別用法註解放置到其他修飾符的後面。
三. 標準註解
(一) 概述
Java SE定義了大量的註解介面,其中四個是元註解,用於描述註解介面的行為屬性,其他三個是規則介面,用來註解程式碼中的項。
(二) 用於編譯的註解
1. @Deprecated
將項標記為過時的,該註解會一直持久化到執行時。
2. @SuppressWarnings
告知編譯器阻止特定型別的警告資訊。
3. @Override
應用到方法上,檢查這種註解的方法是否真正覆蓋了一個來自超類的方法。
4. @Generated
目的是供程式碼生成工具使用,任何生成的原始碼都可以被註解,從而與開發者提供的程式碼區分開來。每個註解都必須包含一個表示程式碼生成器的唯一識別符號。
(三) 用於管理資源的註解
1. @PostConstruct和@PreDestroy
用於控制物件宣告週期的環境中,例如Web容器,標記了這些註解的方法應該在物件被構建之後,或者物件被移除之前,緊接著呼叫。
2. @Resource
用於資源的注入,如配置檔案的注入
(四) 元註解
1. @Target
應用於註解,以限制該註解可以應用到哪些項上面,如方法,類…
@Target({ElementType.TYPE, ElementType.METHOD})
一條沒有@Target限制的註解可以應用於任何項上。
2. @Retention
用於指定一條註解應該保留多長時間,預設是RetentionPolicy.CLSS:
SOURCE:不包含在類檔案中的註解,既不會進入類檔案
CLASS: 包含在類檔案中的註解,但是虛擬機器不需要載入它們
RUNTIME:包含在類檔案中的註解,並且由虛擬機器載入,通過反射API可以獲得它們
3. @Documented
為像Javadoc這樣的歸檔工具提供了提示,用於檔案的歸檔。
4. @Inherited
只能用於對類的註解,如果一個類具有繼承註解,那麼它的所有子類都自動具有相同的註解,這個註解特別適用於Serializable這樣的標記介面。
四. 原始碼級註解處理
(一) 概述
註解的另一種用途是自動處理原始碼以產生更多的原始碼,配置檔案,指令碼或其他任何我們想要生成的東西。
(二) 註解處理器
註解處理已經被整合到Java編譯器中,編譯器會定位原始檔中的註解,每個註解處理器都會依次執行,並得到它表示感興趣的註解,如果某個註解處理器建立了一個新的原始檔,那麼上述過程將會重複執行,如果某次處理迴圈沒有再產生任何新的原始檔,那麼就編譯所有的原始檔。
註解處理器通常通過擴充套件AbstractProcessor類而實現Processor介面,處理器可以宣告具體的註解型別,如下所示:
@SupportedAnnotationTypes(“com.horstmann.annotations.ToString”)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ToStringAnnotationProcessor extends AbstractProcessor{}
五. 位元組碼工程
(一) 概述
處理在執行期或者原始碼級別上處理註解,還可以在位元組碼級別上處理,除非將註解在原始碼級別上刪除,否則它們會一直存在於類檔案中。
(二) 流程
- 載入類檔案中的位元組碼
- 定位所有的方法
- 對於每個方法,檢查它是不是都有某個註解
- 如果有,則在方法中新增某些指令的位元組碼