從新梳理技術棧知識點html
註解是一種特殊的接口,註解繼承自 java.lang.annotation.Annotation
。java
/** The common interface extended by all annotation types. Note that an interface that manually extends this one does not define an annotation type. Also note that this interface does not itself define an annotation type. More information about annotation types can be found in section 9.6 of The Java™ Language Specification. The reflect.AnnotatedElement interface discusses compatibility concerns when evolving an annotation type from being non-repeatable to being repeatable. */
public interface Annotation {
...
}
複製代碼
Annotation
接口的文檔描述:express
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Demo {
}
複製代碼
編譯: javac .\Demo.java 查看字節碼: javap .\Demo.class:json
Compiled from "Demo.java"
public interface org.byron4j.cookbook.javacore.anno.Demo extends java.lang.annotation.Annotation {
複製代碼
經過字節碼,咱們能夠看到註解繼承自 Annotation 接口。數組
咱們要學習一個東西,確定要知道它的做用。相信沒有 Java 程序猿不知道 Spring 框架的,Spring 框架中定義了大量的註解,基於註解,咱們能夠作到幾乎零配置的開發商業項目,如 SpringBoot 就將原來 Spring 使用 XML 進行配置所有轉換爲使用註解達到相同的功效。 如如下是部分註解:安全
註解能夠分爲 元註解、內置註解、用戶自定義註解app
所謂元註解就是,一種基本註解,能夠應用在其它的註解上。能夠這樣理解,元註解是用於標記註解的註解。框架
元註解有:ide
Retention 是保留的意思,代表註解產生的時間範圍。其值是 java.lang.RetentionPolicy
枚舉。函數
@Retention(RetentionPolicy.RUNTIME)
public @interface Demo {
}
複製代碼
@Documented 代表註解的類型將會包含到Javadoc中去。
@Target 標明註解使用約束的應用上下文,是數組形式,能夠標記在多個範圍中使用。值由 java.lang.annotation.ElementType
指定。
java.lang.annotation.ElementType 的可用值以下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
public @interface Demo {
}
複製代碼
Inherited 字面意思是繼承得意思,```@Inherited`` 這個註解將會被自動被繼承。 要求子類沒有被任何註解標記。 只會在extends時纔有效,實現接口是不生效的。給個例子解釋下:
@Inherited
public @interface Demo {
}
@Demo
public class SuperClass{
}
public class ChildClass extends SuperClass{
}
複製代碼
父類 SuperClass 被註解 @Demo 標記了,子類 ChildClass 繼承了父類 SuperClass 且子類沒有被任何註解標記過,則子類會繼承父類的被元註解@Inherited標記的註解 @Demo。
等效於:
@Demo
public class ChildClass extends SuperClass{
}
複製代碼
該元註解標明聲明的註解是能夠屢次使用的,@Repeatable 屬性要求是 註解的class。 如下是一個 Demo註解,表示用戶角色的等級,SSS---》B 共5個等級。
注意: 使用了 @Repeatable 標記了 Demo註解,指定了 Role.class 做爲屬性值,而且 Role 註解必須屬性名爲 value, 且類型爲 Demo 數組形式。
示例以下:
@Repeatable(Role.class)
public @interface Demo {
String role();
}
public @interface Role {
Demo[] value();
}
public @interface Td {
}
@Demo(role="SSS")
@Demo(role="SS")
@Demo(role="S")
@Demo(role="A")
@Demo(role="B")
public class FK1 {
}
@Demo(role="SSS")
@Demo(role="SS")
@Demo(role="S")
@Demo(role="A")
@Demo(role="B")
@Td // 錯誤
@Td // 錯誤 Td註解不能重複出現
public class FK1 {
}
複製代碼
註解只有變量(屬性),沒有方法, 註解的屬性是以 無參方法的形式 聲明的。
public @interface Demo {
String role();
}
複製代碼
@Demo 註解聲明瞭一個屬性 role。
屬性後面使用 default
關鍵字能夠給屬性設置默認值。
public @interface Demo {
String role() default "B";
}
複製代碼
若是隻有一個名爲 value 的屬性,則使用註解的時候能夠直接在圓括號寫屬性值。
public @interface Due {
String value();
}
@Due("Hi")
public class FK1 {
}
// 等效於
@Due(value="Hi")
public class FK1 {
}
複製代碼
沒有屬性的註解,使用時僅僅標記下便可。
public @interface Due {
}
@Due // 僅僅標記下便可
public class FK1 {
}
複製代碼
public class AnnoDemo {
@Deprecated
private static void sayHello(){
}
public static void main(String[] args){
AnnoDemo.sayHello();
}
}
複製代碼
@FunctionalInterface : 函數式接口:一個具備一個方法的普通接口。
@Override : 實現類要重寫父類或者接口的方法
@SafeVarargs : 參數安全類型註解,告訴開發者不要用參數作一些不安全的操做
@SuppressWarnings : 阻止編譯器發出告警,好比調用了使用了 @Deprecated 標記的方法編譯器會發出警告,可使用 @SuppressWarnings 壓制警告
能夠經過 javac -X 命令查看能夠壓制的警告值:
C:\Users\BYRON.Y.Y>javac -X
-Xlint 啓用建議的警告
-Xlint:{all,auxiliaryclass,cast,classfile,deprecation,dep-ann,divzero,empty,fallthrough,finally,options,overloads,overrides,path,processing,rawtypes,serial,static,try,unchecked,varargs,-auxiliaryclass,-cast,-classfile,-deprecation,-dep-ann,-divzero,-empty,-fallthrough,-finally,-options,-overloads,-overrides,-path,-processing,-rawtypes,-serial,-static,-try,-unchecked,-varargs,none} 啓用或禁用特定的警告
複製代碼
@SuppressWarnings 的部分值介紹:
@SuppressWarnings("finally")
public String finallyTest(String str) {
try
{
str+=".";
}
finally
{
return str.toUpperCase();
}
}
複製代碼
註解僅僅是用做標記,要想它真實發揮做用,式利用Java反射機制編寫註解解析器,用做業務需求。
java.lang.reflect.Class
的 isAnnotationPresent()
得知是否存在註解。<T extends Annotation> T getAnnotation(Class<T> annotationClass)
方法獲取註解對象Annotation[] getAnnotations()
方法獲取註解列表@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ByronAnno {
String value() default "ok";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ByronAnno {
String value() default "ok";
}
複製代碼
運行輸出:註解值爲:類
最後以一個生產案例的使用,來結束對註解的介紹。
案例: http接口中,請求參數是字符串形式,將請求參數轉換爲請求實體類。對參數進行校驗時,須要檢查某些字段是否爲空,以及整型數值的大小校驗。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateVal {
String length() default "";
boolean isBeNull() default false;
boolean isNotcheckDecimal() default false;// 驗證小數
int isJudgeLength() default 0; // 0 :不判斷 1:定值 2:區間
int minLength() default 0;
int maxLength() default 20;
int minValue() default 0;
int maxValue() default Integer.MAX_VALUE;
String expression() default "";
String errorMsg() default "";
int neeDivided() default 1; // 是否須要倍數
//是否校驗數字,是=true,否=false
boolean isCheckNumber() default false;
}
複製代碼
protected <T> T validateAndConvert(String param, Class<T> cla) throws Exception {
T t = JSONUtils.json2Object(param, cla);
if (t == null) {
// 拋出異常
}
Field[] fields = t.getClass().getDeclaredFields();
Field[] parentFields = t.getClass().getSuperclass().getDeclaredFields();
List<Field> allFields = Lists.newArrayList(fields);
allFields.addAll(Arrays.asList(parentFields));
for (Field f : allFields) {
f.setAccessible(true);
ValidateVal an = f.getAnnotation(ValidateVal.class);
String fieldName = f.getName();
if (an == null) {
String value = String.valueOf(f.get(t));
value = value.trim();
if (f.getType().equals(String.class)) {
f.set(t, value);
}
if (value == null || value.equals("") || value.equals("null")) {
// 拋出異常
}
} else {
if (f.getType().equals(String.class)) {
String value = null;
if (f.get(t) != null) {
value = String.valueOf(f.get(t));
value = value.trim();
f.set(t, value);
}
if (!an.isBeNull()) {
if (value == null || value.equals("") || value.equals("null")) {
// 拋出異常
}
} else {// 爲空串置爲null
if (value == null || value.equals("") || value.equals("null")) {
f.set(t, null);
}
}
if (!an.expression().equals("")) {
Pattern pattern = Pattern.compile(an.expression());
Matcher matcher = pattern.matcher(value);
if (!matcher.matches()) {
// 拋出異常
}
}
if (an.isJudgeLength() == 1) { // 定值
String[] lengthArr = an.length().split(",");
boolean in = false;
for (int i = 0; i < lengthArr.length; i++) {
if (value.length() == Integer.parseInt(lengthArr[i])) {
in = true;
}
}
if (!in) {
// 拋出異常
}
} else if (an.isJudgeLength() == 2) {
int min = an.minLength();
int max = an.maxLength();
if (value.length() < min || value.length() > max) {
// 拋出異常
}
}
} else if (f.getType().equals(Integer.class)) {
if (f.get(t) == null) {
if (an.isBeNull()) {
f.set(t, null);
continue;
} else {
// 拋出異常
}
}
Integer value = Integer.valueOf(f.get(t).toString());
if (an.neeDivided() != 1 && value % an.neeDivided() != 0) {
// 拋出異常
}
if (value < an.minValue() || value > an.maxValue()) {
// 拋出異常
}
}
}
}
return t;
}
複製代碼
參考資料: