從理論到應用,你需要掌握的Java注解知識都在這里

1.引言

前段時間因為業(yè)務的需求,需要對律所的數(shù)據(jù)進行拆分與合并,因為整個系統(tǒng)涉及到大量的業(yè)務模塊,為了盡量減少各具體模塊的遷移開發(fā)工作量,需要對遷移過程中的共性內(nèi)容進行抽象,從而設(shè)計出一套更通用的遷移框架。

遷移框架在DB層面進行復制,為了更方便的描述表結(jié)構(gòu)及表之間的關(guān)聯(lián),使用了注解進行定義,故總結(jié)一下注解相關(guān)的知識。

注解是JDK1.5引入的新特性,是在代碼里面的一種特殊標記,針對這些標記,我們可以在源碼、編譯或運行時對代碼做特殊處理。目前很多主流框架都有在使用注解,如:

JDK里面的注解@Override、@Resource等;

Spring里面使用的注解@Autowired、@Configurable等;

JUnit測試框架里面的@Test、@Before、@BeforeClass等。

可以說了解注解是我們設(shè)計出優(yōu)雅代碼,通讀各類框架源碼的必備技能,尤其是在Spring Boot大行其道的今天。

2.自定義注解

2.1 定義及使用

以下代碼定義了一個注解Table,有一個屬性value用來表示表的名稱。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public @interface Table{

/**

* 表的名稱

*/

String value() default "";

}

以下代碼使用了剛才定義的注解Table,指定了value的屬性值app_matter,因為只有一個屬性,所以沒有寫全稱value="app_matter"

@Table("app_matter")

public class MatterDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

定義注解的過程中,還存在很多其他信息,下面的章節(jié)對定義注解中涉及的各個信息進行說明。

2.2 元注解

上面在定義注解的過程中,使用到了很多其他注解,這種在描述注解的注解,稱之為元注解。

@Target

定義當前注解可以使用在什么地方,上面的Table注解Target使用了TYPE,所以可以應用到類、接口、注解及枚舉中,其他Target的類型還有:

其中TYPE_PARAMETER、TYPE_USE是1.8的新增特性,因為并沒有提供反射接口獲取處理,所以該注解一般要開發(fā)者自己實現(xiàn)或第三方開發(fā)工具實現(xiàn)。

ElementType.TYPE_PARAMETER:

表示該注解可以應用到類型參數(shù)的申明中,如:

public <@MyTypeParameterT> void show(Tmessage){}

ElementType.TYPE_USE:

表示該注解可以使用到類型出現(xiàn)的地方,如:

MatterDefinition instance = new @MyTypeUse MatterDefinition();。

@Retention

定義當前注解的保留策略,如:

RetentionPolicy.SOURCE:

表示該注解僅存在源碼中,編譯成class文件后,注解信息將丟失,如:@Override注解。

RetentionPolicy.CLASS:

表示該注解存在編譯后的class文件中,是注解保留策略的默認方式。

RetentionPolicy.RUNTIME:

表示該注解存在class文件中,并且被jvm加載后還存在,可以使用反射相關(guān)接口進行獲取。

@Documented

定義該注解是否支持導出到j(luò)avadoc文檔中。

@Inherited

定義該注解是否可以被子類繼承,如:ClassA使用了Inherited注解,ClassB繼承了ClassA,則ClassB可以獲取到該注解。

2.3 注解元素

上例中的注解Table,定義了元素value

String value() default "";

3.重復注解

Repeatable:重復注解,JDK1.8引入的新特性,可以在類或方法等上指定多個相同的注解屬性,這種方式只是一種語法糖,下面的Demo類中最終生成的注解其實還是Items。

@Target(ElementType.TYPE)

public @interface Items{

Item[] value();

}

@Repeatable(Items.class)

@Target(ElementType.TYPE)

@interface Item{

String value() default "";

}

@Item("item1")

@Item("item2")

class Demo {

? ? public static void main(String[] args) {

? ? }

}

4.注解反射

和注解相關(guān)的接口有Annotation和AnnotatedElement,其中:

所有的注解對象annotation都實現(xiàn)了Annotation接口,該對象的annotationType方法返回注解的類型(如:Table.class),該對象自身是一個動態(tài)代理對象(如:$Proxy2)。

5.注解的應用

5.1 注解屬性的獲取

@Table("app_matter")

class MatterDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

public class AnnotationDemo {

public static void main(String[] args)throws NoSuchFieldException {

System.out.println(MatterDefinition.class.isAnnotationPresent(Table.class));//true

Table tableAnnotation = MatterDefinition.class.getAnnotation(Table.class);

System.out.println(tableAnnotation.value());//app_task

Field idField = MatterDefinition.class.getDeclaredField("id");

Id idAnnotation = idField.getAnnotation(Id.class);

System.out.println(idAnnotation.mode());//IdGeneratorMode.UUID

}

}

5.2 獲取指定包下含有指定注解的類

方式一:原生反射

通過JDK原生的方式進行反射處理,即獲取到指定包下的所有類,然后遍歷每個類判斷當前類是否出現(xiàn)指定注解,這種方式相當于從零開始撰寫很多基礎(chǔ)代碼。

方式二:借助Spring中注解Component的Bean管理

在定義注解(如:Table)的時候加上元注解@Component,以把使用當前注解的類注冊為Bean對象

從容器ApplicationContext中調(diào)用:Map getBeansWithAnnotation(Class annotationType)

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Component//此處加上Component注解

@interface MyTableAnnotation{

String value() default "";

}

@MyTableAnnotation("app_task")

class TaskDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

@Component

public class ComponentAnnotationDemo implements ApplicationContextAware {

ApplicationContext applicationContext;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext= applicationContext;

}

public void show() {

//獲取容器中指定注解類型的所有Bean對象

Map stringObjectMap =applicationContext.getBeansWithAnnotation(MyTableAnnotation.class);

System.out.println("stringObjectMap:"+ stringObjectMap);

}

}

方式三:借助Spring的包掃描注解ClassPathScanningCandidateComponentProvider

通過ClassPathScanningCandidateComponentProvider掃描指定的包

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@interface MyTableAnnotation{

String value() default "";

}

@MyTableAnnotation("app_task")

class TaskDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

@Component

public class ComponentAnnotationDemo {

public void show() {

//useDefaultFilters:是否掃描默認的Component,Controller,Service,Repository注解

ClassPathScanningCandidateComponentProvider classPathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);

//以注解的方式作為掃描條件,也可以指定正則(如:RegexPatternTypeFilter)等其他掃描方式

classPathScanningCandidateComponentProvider.addIncludeFilter(newAnnotationTypeFilter(MyTableAnnotation.class));

//basePackage:指定掃描的基礎(chǔ)包,空字符串為所有包

Set beanDefinitionSet = classPathScanningCandidateComponentProvider.findCandidateComponents("com.icourt");

System.out.println(beanDefinitionSet);

}

}

6.幾個注解的簡單說明

此處簡單列舉幾個注解的說明,權(quán)當混個臉熟,后期再根據(jù)實際情況,結(jié)合各框架,進行專題深入講解。

以下三個注解是JDK1.5開始支持注解特性時引入的,也算是鼻祖了。

Override:表示重寫了父類的指定方法;如果方法上有該注解,但方法名與超類不同,開發(fā)工具將給出錯誤警告。

Deprecated:表示指定的類或方法已經(jīng)被廢棄,不建議繼續(xù)使用,而應該使用新的替代方案。

SuppressWarnings:表示忽略指定類型的警告提示。

以下兩個注解是JDK1.8引入的新特性。

FunctionalInterface:函數(shù)式接口的注解,主要用在Lambda表達式中,表示該接口只能有一個抽象方法,從而可以使用Lambda表達式進行簡寫。

Repeatable:重復注解,可以在類或方法等上指定多個相同的注解屬性,上面章節(jié)有說明。

以下三個是通用注解(Common Annotation),原來是Java EE5.0規(guī)范的一部分,后面加入到了Java SE6.0中,避免框架或各類開發(fā)者重復定義。Java SE里面僅包含了注解類的定義,但沒有包含注解類的解析實現(xiàn),而是由Java EE容器進行實現(xiàn),如:應用到Servlet規(guī)范的生命周期注解;Spring對這幾個注解也做了解析支持。

Resource:表示一種資源,如果應用到屬性或方法上,則這個類在實例化的時候,將自動注入該類型或命名的對象。

PostConstruct:表示在對象創(chuàng)建之后做的額外處理。

PreDestroy:表示在對象銷毀之前做的額外處理。

以下注解是Spring2.5版本引入的,針對MVC和通用組件的一套注解。

Controller:表示這是一個Controller層對象,一般封裝參數(shù)校驗,權(quán)限判斷,及不可復用的簡單邏輯。

Service:表示這是一個業(yè)務層Bean對象,一般封裝具體的業(yè)務邏輯。

Repository:Spring 2.0引入的注解,對數(shù)據(jù)訪問層的對象進行Bean標示。

Component:表示這是一個Bean對象。

Autowired:默認根據(jù)類型自動裝配指定的Bean對象,如果要按名稱進行裝配,可以結(jié)合Qualifier注解。

以下兩個注解是Spring3.0為了減少XML配置而引入的基于Java的注解。

Configuration:表示這是一個配置類,里面會包含一個或多個被Bean標注的方法,用于Spring創(chuàng)建出Bean的實例。

Bean:標注的方法可以構(gòu)造出Bean的實例,方法名默認為容器中Bean的名稱。

以下為Spring4.0引入的條件注解。

Conditional:根據(jù)某個條件決定是否創(chuàng)建某個bean,這也是Spring Boot框架實現(xiàn)的基礎(chǔ)。

7.要點總結(jié)

對前面介紹的內(nèi)容做一張腦圖進行總結(jié),方便大家一覽文章的關(guān)鍵要點。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,724評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,900評論 18 399
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,800評論 15 116
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,518評論 2 7
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,039評論 0 11

友情鏈接更多精彩內(nèi)容