創(chuàng)建自定義的Java注解類的方法
如果你已經(jīng)在使用Java編程,并且也使用了任何像Spring和Hibernate這樣的流行框架,那么你應(yīng)該對(duì)注解的使用非常地熟悉。使用一個(gè)現(xiàn)有框架工作的時(shí)候,通常使用它的注解就夠了。但是,你是不是也有時(shí)候有要?jiǎng)?chuàng)建屬于你自己的注解的需求呢?
不久之前,我找到了一個(gè)自己創(chuàng)建一個(gè)注解的理由,那是一個(gè)涉及驗(yàn)證存儲(chǔ)在多種數(shù)據(jù)庫中的常用數(shù)據(jù)的項(xiàng)目。
場(chǎng)景描述
該業(yè)務(wù)有多種數(shù)據(jù)庫都存儲(chǔ)著相同的數(shù)據(jù),它們有各自不同的保持?jǐn)?shù)據(jù)更新的方法. 該業(yè)務(wù)曾計(jì)劃把所有這些數(shù)據(jù)都整合到一個(gè)主數(shù)據(jù)庫中,以減輕涉及到多種數(shù)據(jù)源所帶來的問題的復(fù)雜性.
不過在項(xiàng)目開始之前,業(yè)務(wù)還需要知道數(shù)據(jù)距離可以同步還有多少差距,并做出任何必要的修正來使其可以進(jìn)行同步. 第一步需要?jiǎng)?chuàng)建一個(gè)展示那些數(shù)據(jù)多種數(shù)據(jù)庫的通用數(shù)據(jù)的報(bào)表,并對(duì)其值進(jìn)行驗(yàn)證, 對(duì)那些不符合條件的記錄進(jìn)行高亮顯示. 這里有一個(gè)對(duì)當(dāng)時(shí)需求的簡(jiǎn)短摘要:
- 比對(duì)多種數(shù)據(jù)庫間公共部分的數(shù)據(jù),諸如客戶,公司或者目錄信息.
- 默認(rèn)的值應(yīng)該根據(jù)值的類型匹配所有的數(shù)據(jù)庫.
- 對(duì)于某些字段,我們只想展示其值,而不要進(jìn)行任何數(shù)據(jù)比較.
- 對(duì)于某些字段,我們只想要對(duì)比其值,并在指定的特定數(shù)據(jù)源上進(jìn)行數(shù)據(jù)驗(yàn)證.
- 對(duì)于某些字段,我們可能想要做一些復(fù)雜的數(shù)據(jù)比較,可能會(huì)基于記錄內(nèi)的其它字段.
- 對(duì)于某些字段,我們可能想要用一種特定格式對(duì)數(shù)據(jù)進(jìn)行格式化,比如錢幣數(shù)量 使用 $000,000.00 .
- 報(bào)表應(yīng)該用MS Excel格式的,每一行都包含來自每個(gè)數(shù)據(jù)源的字段值. 任何不匹配數(shù)據(jù)驗(yàn)證規(guī)則的行都應(yīng)該用黃色高亮顯示.
注解
經(jīng)過一陣子對(duì)需求和一些想法的推敲之后,我決定使用注解來驅(qū)動(dòng)對(duì)于數(shù)據(jù)比對(duì)和報(bào)表處理的配置. 我們需要的東西得是簡(jiǎn)單,而高度靈活可擴(kuò)展的. 這些注解將會(huì)是字段級(jí)別的,而我就喜歡配置不會(huì)被隱藏在classpath某個(gè)地方的文件中. 如此,你就能夠直接查看同一個(gè)字段相關(guān)聯(lián)的注解,以便知曉它具體是如何進(jìn)行處理的.
在最簡(jiǎn)單的情況下,注解無非就是一個(gè)標(biāo)記,就只是提供信息而不會(huì)對(duì)代碼執(zhí)行的操作本身有直接影響的元數(shù)據(jù). 如果你一直在從事Java編程,那么現(xiàn)在你對(duì)它們的使用應(yīng)該相當(dāng)?shù)氖煜ち? 但是可能你從來沒有過創(chuàng)建屬于你自己的注解的需求. 為此,你需要?jiǎng)?chuàng)建一個(gè)帶有Java類型@interface的新類型,它將包含能指定元數(shù)據(jù)詳細(xì)信息的要素.
這里有一個(gè)來自這個(gè)項(xiàng)目的示例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReconField {
/**
* Value indicates whether or not the values from the specified sources should be compared or will be used to display values or reference within a rule.
*
* @return The value if sources should be compared, defaults to true.
*/
boolean compareSources() default true;
/**
* Value indicates the format that should be used to display the value in the report.
*
* @return The format specified, defaulting to native.
*/
ReconDisplayFormat displayFormat() default ReconDisplayFormat.NATIVE;
/**
* Value indicates the ID value of the field used for matching source values up to the field.
*
* @return The ID of the field.
*/
String id();
/**
* Value indicates the label that should be displayed in the report for the field.
*
* @return The label value specified, defaults to an empty string.
*/
String label() default "";
/**
* Value that indicates the sources that should be compared for differences.
*
* @return The list of sources for comparison.
*/
ReconSource[] sourcesToCompare() default {};
}
這是驅(qū)動(dòng)數(shù)據(jù)比對(duì)過程如何運(yùn)作的主要注解. 它包含的基本要素,可以滿足不同數(shù)據(jù)源間數(shù)據(jù)進(jìn)行比較的大部分需求. @ReconField 可以處理除更加復(fù)雜的比對(duì)之外,我們所期望的大多數(shù)需求, 而更加復(fù)雜的情況我們將會(huì)在稍后有所討論. 這些要素的大多數(shù)在代碼清單中一對(duì)一的注釋中都有介紹, 而需要指出的是,在我們的@ReconField上有幾個(gè)關(guān)鍵的注解.
@Target – 這個(gè)注解可以讓你來指定你的注解應(yīng)該被用在那個(gè)java元素上. 可能的目標(biāo)類型是 ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER 和 TYPE. 在我們的 @ReconField 注解中他被指定到了 FIELD 級(jí)別.
@Retention – 它可以讓你指定注解在何時(shí)生效. 可能的值有 CLASS, RUNTIME 和 SOURCE. 因?yàn)槲覀儗?huì)在運(yùn)行時(shí) RUNTIME 處理這個(gè)注解, 所以那就是我們需要設(shè)置的值.
這一數(shù)據(jù)驗(yàn)證過程將會(huì)為每一個(gè)數(shù)據(jù)庫運(yùn)行一次查詢,并且將結(jié)果映射到展示出針對(duì)特定業(yè)務(wù)記錄類型所有字段的實(shí)體bean中. 映射數(shù)據(jù)實(shí)體的每一個(gè)字段上的注解會(huì)告訴處理器如何為特定字段及在每個(gè)數(shù)據(jù)庫中找到的其值執(zhí)行數(shù)據(jù)比對(duì). 因此讓我們來看幾個(gè)示例來了解這些注解是如何被運(yùn)用于不同的數(shù)據(jù)比對(duì)配置的.
為了驗(yàn)證現(xiàn)有的值并同每個(gè)數(shù)據(jù)源中的只精確匹配,你只需要提供一個(gè)字段ID以及將會(huì)展示在報(bào)表上字段的標(biāo)記.
@ReconField(id = CUSTOMER_ID, label = "Customer ID") private String customerId;
為了展示在每個(gè)數(shù)據(jù)源中找到的值,但不做任何數(shù)據(jù)比對(duì),你可能需要制定 compareSources 元素,并將其值設(shè)置為false.
@ReconField(id = NAME, label = "NAME", compareSources = false) private String name;
為了驗(yàn)證在指定數(shù)據(jù)源中找到的值,而不是全部,你可能會(huì)使用到 elementsourcesToCompare. 使用這個(gè)東西會(huì)展示所有找到的值,但是只對(duì)在元素中列出的數(shù)據(jù)源中找到的值進(jìn)行比對(duì). 這樣就能處理有些不是在每一個(gè)數(shù)據(jù)源中都會(huì)存儲(chǔ)的數(shù)據(jù)場(chǎng)景了. ReconSource 是一個(gè)包含了可以用來進(jìn)行比對(duì)的數(shù)據(jù)源的枚舉類型.
@ReconField(id = PRIVATE_PLACEMENT_FLAG, label = "PRIVATE PLACEMENT FLAG", sourcesToCompare ={ ReconSource.LEGACY, ReconSource.PACE })
private String privatePlacementFlag;
現(xiàn)在我們已經(jīng)滿足了我們的基本需求,我們需要解決實(shí)現(xiàn)指定字段來進(jìn)行復(fù)雜數(shù)據(jù)比對(duì)能力的問題. 為此,我們將創(chuàng)建第二個(gè)注解,來驅(qū)動(dòng)定制規(guī)則處理.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReconCustomRule {
/**
* Value indicates the parameters used to instantiate a custom rule processor, the default value is no parameters.
*
* @return The String[] of parameters to instantiate a custom rule processor.
*/
String[] params() default {};
/**
* Value indicates the class of the custom rule processor to be used in comparing the values from each source.
*
* @return The class of the custom rule processor.
*/
Class<?> processor() default DefaultReconRule.class;
}
同之前的注解非常類似,最大的不同在于 @ReconCustomRule 注解中我們指定了一個(gè)類,它可以在重建處理執(zhí)行時(shí)執(zhí)行數(shù)據(jù)比對(duì). 你可以只定義將會(huì)被用到的類,那樣你的處理器就可以實(shí)例化并初始化你所指定的類. 在這個(gè)注解中指定的類將需要實(shí)現(xiàn)一個(gè)通用的規(guī)則接口,它將會(huì)被規(guī)則處理器用來執(zhí)行規(guī)則.
現(xiàn)在讓我們來看看使用這個(gè)注解 的例子.
在本例中,我們使用了一個(gè)自定義的規(guī)則,它將會(huì)檢查股票交易所是不是 United States,如果是則跳過這條數(shù)據(jù). 為此,這條規(guī)則將需要檢查同一記錄中的 exchange country 字段.
@ReconField(id = STREET_CUSIP, label = "STREET CUSIP", compareSources = false) @ReconCustomRule(processor = SkipNonUSExchangeComparisonRule.class) private String streetCusip;
這里的示例我們?yōu)樽远x規(guī)則指定了一個(gè)參數(shù),這里它是一個(gè)包容量. 對(duì)于這種特殊的數(shù)據(jù)比對(duì),被比較的值不能偏離超過1000.通過使用指定了包容量的參數(shù),我們就可以使用不同的包容量將同一套自定義規(guī)則運(yùn)用到多個(gè)字段上. 唯一的缺點(diǎn)就是,由于注解的性質(zhì),這些參數(shù)都只能是靜態(tài)的,所以不能動(dòng)態(tài)的修改.
@ReconField(id = USD_MKT_CAP, label = "MARKET CAP USD", displayFormat = ReconDisplayFormat.NUMERIC_WHOLE, sourcesToCompare =
{ ReconSource.LEGACY, ReconSource.PACE, ReconSource.BOB_PRCM })
@ReconCustomRule(processor = ToleranceAmountRule.class, params = { "10000" })
private BigDecimal usdMktCap;
如你所見,我們只使用了幾個(gè)簡(jiǎn)單的注解,就設(shè)計(jì)出了一個(gè)具有相當(dāng)程度靈活性的面向多數(shù)據(jù)庫場(chǎng)景的數(shù)據(jù)驗(yàn)證報(bào)告功能. 在這個(gè)特殊情況下,注解驅(qū)動(dòng)了數(shù)據(jù)的比對(duì)過程,因此我們實(shí)際上就是使用了注解在找到的映射數(shù)據(jù)實(shí)體上進(jìn)行計(jì)算并直接使用它們進(jìn)行處理.
- Java注解之Retention、Documented、Inherited介紹
- java教程之java注解annotation使用方法
- 5分鐘搞懂java注解@Annotation的具體使用
- Java注解Annotation與自定義注解詳解
- Java注解機(jī)制之Spring自動(dòng)裝配實(shí)現(xiàn)原理詳解
- Java注解@Transactional事務(wù)類內(nèi)調(diào)用不生效問題及解決辦法
- 基于Java注解(Annotation)的自定義注解入門介紹
- 詳解Java注解教程及自定義注解
- 深入理解Java注解類型(@Annotation)
- 輕松掌握J(rèn)ava注解,讓編程更智能、更優(yōu)雅
相關(guān)文章
給@Value設(shè)置默認(rèn)值以及為static變量賦值問題
在Spring框架中,@Value注解用于屬性注入,可將配置文件中的值賦給變量,未指定默認(rèn)值時(shí),若配置文件缺少相應(yīng)屬性,程序啟動(dòng)會(huì)報(bào)錯(cuò),可通過設(shè)定默認(rèn)值防止此問題,對(duì)于靜態(tài)變量,由于@Value無法直接注入,需通過Set方法賦值,該方法也支持默認(rèn)值設(shè)置2024-09-09
了解Java虛擬機(jī)JVM的基本結(jié)構(gòu)及JVM的內(nèi)存溢出方式
這篇文章主要介紹了Java虛擬機(jī)JVM的基本結(jié)構(gòu)及JVM的內(nèi)存溢出方式,涉及到Java內(nèi)存分配相關(guān)方面的知識(shí),需要的朋友可以參考下2016-01-01
Java線程之線程同步synchronized和volatile詳解
這篇文章主要介紹了Java線程之線程同步synchronized和volatile詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
springboot 啟動(dòng)如何修改application.properties的參數(shù)
這篇文章主要介紹了springboot 啟動(dòng)如何修改application.properties的參數(shù)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Spring Boot中自定義注解結(jié)合AOP實(shí)現(xiàn)主備庫切換問題
這篇文章主要介紹了Spring Boot中自定義注解+AOP實(shí)現(xiàn)主備庫切換的相關(guān)知識(shí),本篇文章的場(chǎng)景是做調(diào)度中心和監(jiān)控中心時(shí)的需求,后端使用TDDL實(shí)現(xiàn)分表分庫,需要的朋友可以參考下2019-08-08
SpringBoot+Redis實(shí)現(xiàn)消息的發(fā)布與訂閱的示例代碼
本文主要介紹了SpringBoot+Redis實(shí)現(xiàn)消息的發(fā)布與訂閱,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
spring boot openfeign從此和httpClient說再見詳析
這篇文章主要給大家介紹了關(guān)于spring boot openfeign從此和httpClient說再見的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧2018-06-06

