1.引言
前段時間因為業(yè)務(wù)的需求颜凯,需要對律所的數(shù)據(jù)進行拆分與合并,因為整個系統(tǒng)涉及到大量的業(yè)務(wù)模塊仗扬,為了盡量減少各具體模塊的遷移開發(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痕寓,所以可以應(yīng)用到類傲醉、接口、注解及枚舉中呻率,其他Target的類型還有:
其中TYPE_PARAMETER硬毕、TYPE_USE是1.8的新增特性,因為并沒有提供反射接口獲取處理礼仗,所以該注解一般要開發(fā)者自己實現(xiàn)或第三方開發(fā)工具實現(xiàn)吐咳。
ElementType.TYPE_PARAMETER:
表示該注解可以應(yīng)用到類型參數(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.注解的應(yīng)用
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ù)使用蟆沫,而應(yīng)該使用新的替代方案。
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),如:應(yīng)用到Servlet規(guī)范的生命周期注解挪凑;Spring對這幾個注解也做了解析支持孕索。
Resource:表示一種資源,如果應(yīng)用到屬性或方法上躏碳,則這個類在實例化的時候搞旭,將自動注入該類型或命名的對象。
PostConstruct:表示在對象創(chuàng)建之后做的額外處理菇绵。
PreDestroy:表示在對象銷毀之前做的額外處理肄渗。
以下注解是Spring2.5版本引入的,針對MVC和通用組件的一套注解咬最。
Controller:表示這是一個Controller層對象翎嫡,一般封裝參數(shù)校驗,權(quán)限判斷永乌,及不可復用的簡單邏輯惑申。
Service:表示這是一個業(yè)務(wù)層Bean對象,一般封裝具體的業(yè)務(wù)邏輯翅雏。
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)鍵要點。