国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

深入淺析Java注解框架

2019-11-26 14:17:47
字體:
來源:轉載
供稿:網友

我們經常會在java代碼里面看到:“@Override”,“@Target”等等樣子的東西,這些是什么?

在java里面它們是“注解”。

下面是百度百科的解釋:java.lang.annotation.Retention可以在您定義Annotation型態時,指示編譯器如何對待您的自定義 Annotation,預設上編譯器會將Annotation資訊留在class檔案中,但不被虛擬機器讀取,而僅用于編譯器或工具程式運行時提供資訊。

也就是說,注解是建立在class文件基礎上的東西,同C語言的宏有異曲同工的效果。

class文件里面根本看不到注解的痕跡。

注解的基礎就是反射。所以注解可以理解為java特有的一種概念。

1.元注解

在java.lang.annotation包里面,已經定義了4種annotation的“原語”。

1).@Target,用于明確被修飾的類型:(方法,字段,類,接口等等)  

2).@Retention,描述anntation存在的為止:

RetentionPolicy.RUNTIME 注解會在class字節碼文件中存在,在運行時可以通過反射獲取到

RetentionPolicy.CLASS 默認的保留策略,注解會在class字節碼文件中存在,但運行時無法獲得

RetentionPolicy.SOURCE 注解僅存在于源碼中,在class字節碼文件中不包含

3).@Documented,默認情況下,注解不會在javadoc中記錄,但是可以通過這個注解來表明這個注解需要被記錄。

4).@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。

如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。

2.自定義注解

package com.joyfulmath.jvmexample.annnotaion;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** @author deman.lu* @version on 2016-05-23 13:36*/@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface FruitName {String value() default "";} 

首先,一個注解一般需要2個元注解修飾:

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

具體作用上面已解釋。

所有的注解都會有一個類似于“func”的部分。這個可以理解為注解的參數。

package com.joyfulmath.jvmexample.annnotaion;import com.joyfulmath.jvmexample.TraceLog;/*** @author deman.lu* @version on 2016-05-23 13:37*/public class Apple {@FruitName("Apple")String appleName;public void displayAppleName(){TraceLog.i(appleName);}}

這段代碼的log:

05-23 13:39:38.780 26792-26792/com.joyfulmath.jvmexample I/Apple: displayAppleName: null [at (Apple.java:16)]

沒有賦值成功,為什么?應為注解的“Apple”到底怎么賦值該filed,目前編譯器還不知道則怎么做呢。

3.注解處理器

我們還需要一個處理器來解釋 注解到底是怎樣工作的,不然就跟注釋差不多了。

通過反射的方式,可以獲取注解的內容:

package com.joyfulmath.jvmexample.annnotaion;import com.joyfulmath.jvmexample.TraceLog;import java.lang.reflect.Field;/*** @author deman.lu* @version on 2016-05-23 14:08*/public class FruitInfoUtils {public static void getFruitInfo(Class<?> clazz){String fruitNameStr = "";Field[] fields = clazz.getDeclaredFields();for(Field field:fields){if(field.isAnnotationPresent(FruitName.class)){FruitName fruitName = field.getAnnotation(FruitName.class);fruitNameStr = fruitName.value();TraceLog.i(fruitNameStr);}}}}

這是注解的一般用法。

android注解框架解析

從上面可以看到,注解框架的使用,本質上還是要用到反射。

但是我如果用反射的功能在使用注解框架,那么,我還不如直接使用它,反而簡單。

如果有一種機制,可以避免寫大量重復的相似代碼,尤其在android開發的時候,大量的findviewbyid & onClick等事件相應。

代碼的模式是一致的,但是代碼又各不相同,這個時候,使用注解框架可以大量節省開發時間,當然相應的會增加其他的開銷。

以下就是一個使用butterknife的例子:

@BindString(R.string.login_error)
String loginErrorMessage;

看上去很簡單,就是把字符串賦一個string res對應的初值。這樣寫可以節省一些時間。當然這只是一個例子,

如果大量使用其他的注解,可以節省很大一部分的開發時間。

我們下面來看看怎么實現的:

package butterknife;import android.support.annotation.StringRes;import java.lang.annotation.Retention;import java.lang.annotation.Target;import static java.lang.annotation.ElementType.FIELD;import static java.lang.annotation.RetentionPolicy.CLASS;/*** Bind a field to the specified string resource ID.* <pre><code>* {@literal @}BindString(R.string.username_error) String usernameErrorText;* </code></pre>*/@Retention(CLASS) @Target(FIELD)public @interface BindString {/** String resource ID to which the field will be bound. */@StringRes int value();}

BindString,只有一個參數,value,也就是賦值為@StringRes.

同上,上面是注解定義和使用的地方,但是真正解釋注解的地方如下:ButterKnifeProcessor

private Map<TypeElement, BindingClass> findAndParseTargets(RoundEnvironment env)

這個函數,截取部分代碼:

// Process each @BindString element.for (Element element : env.getElementsAnnotatedWith(BindString.class)) {if (!SuperficialValidation.validateElement(element)) continue;try {parseResourceString(element, targetClassMap, erasedTargetNames);} catch (Exception e) {logParsingError(element, BindString.class, e);}}

找到所有BindString注解的元素,然后開始分析:

private void parseResourceString(Element element, Map<TypeElement, BindingClass> targetClassMap,Set<TypeElement> erasedTargetNames) {boolean hasError = false;TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();// Verify that the target type is String.if (!STRING_TYPE.equals(element.asType().toString())) {error(element, "@%s field type must be 'String'. (%s.%s)",BindString.class.getSimpleName(), enclosingElement.getQualifiedName(),element.getSimpleName());hasError = true;}// Verify common generated code restrictions.hasError |= isInaccessibleViaGeneratedCode(BindString.class, "fields", element);hasError |= isBindingInWrongPackage(BindString.class, element);if (hasError) {return;}// Assemble information on the field.String name = element.getSimpleName().toString();int id = element.getAnnotation(BindString.class).value();BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);FieldResourceBinding binding = new FieldResourceBinding(id, name, "getString", false);bindingClass.addResource(binding);erasedTargetNames.add(enclosingElement);}

首先驗證element是不是string類型。

// Assemble information on the field.String name = element.getSimpleName().toString();int id = element.getAnnotation(BindString.class).value(); 

獲取field的name,以及 string id。

最終

Map<TypeElement, BindingClass> targetClassMap

元素和注解描述,已map的方式一一對應存放。

@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env);for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {TypeElement typeElement = entry.getKey();BindingClass bindingClass = entry.getValue();try {bindingClass.brewJava().writeTo(filer);} catch (IOException e) {error(typeElement, "Unable to write view binder for type %s: %s", typeElement,e.getMessage());}}return true;}

這就是注解框架啟動的地方,一個獨立的進程。具體細節本文不研究,只需清除,這里是框架驅動的地方。

從上面的信息已經清除,所有的注解信息都存放在targetClassMap 里面。

上面標紅的代碼,應該是注解框架的核心之處。

自從Java SE5開始,Java就引入了apt工具,可以對注解進行預處理,Java SE6,更是支持擴展注解處理器,

并在編譯時多趟處理,我們可以使用自定義注解處理器,在Java編譯時,根據規則,生成新的Java代碼。

JavaFile brewJava() {TypeSpec.Builder result = TypeSpec.classBuilder(generatedClassName).addModifiers(PUBLIC);if (isFinal) {result.addModifiers(Modifier.FINAL);} else {result.addTypeVariable(TypeVariableName.get("T", targetTypeName));}TypeName targetType = isFinal ? targetTypeName : TypeVariableName.get("T");if (hasParentBinding()) {result.superclass(ParameterizedTypeName.get(parentBinding.generatedClassName, targetType));} else {result.addSuperinterface(ParameterizedTypeName.get(VIEW_BINDER, targetType));}result.addMethod(createBindMethod(targetType));if (isGeneratingUnbinder()) {result.addType(createUnbinderClass(targetType));} else if (!isFinal) {result.addMethod(createBindToTargetMethod());}return JavaFile.builder(generatedClassName.packageName(), result.build()).addFileComment("Generated code from Butter Knife. Do not modify!").build();}

這段話的關鍵是會create一個新文件。

然后把相關內容寫入。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 新干县| 宜兰县| 西城区| 随州市| 通化县| 江华| 高陵县| 苍山县| 巴林左旗| 阿拉善右旗| 京山县| 阳泉市| 龙山县| 台东县| 宜良县| 晋城| 海兴县| 北安市| 太和县| 石棉县| 汶川县| 武宁县| 光泽县| 逊克县| 丁青县| 乐平市| 隆子县| 高密市| 平塘县| 岫岩| 克东县| 广南县| 逊克县| 揭阳市| 安泽县| 丁青县| 凤阳县| 民勤县| 安塞县| 安徽省| 河西区|