程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 深刻淺析Java注解框架

深刻淺析Java注解框架

編輯:關於JAVA

深刻淺析Java注解框架。本站提示廣大學習愛好者:(深刻淺析Java注解框架)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻淺析Java注解框架正文


我們常常會在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一個新文件。

然後把相干內容寫入。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved