目錄
並非所有的註解都相同
為什麼有害的註解不好?
企業陷阱
有害的註解有時會出現在錯誤的地點
有害的注解有时蔓延
有什么替代品?
总结
首頁 Java java教程 Java中萬惡的註解

Java中萬惡的註解

Feb 20, 2017 am 10:09 AM
java 註解

本文由碼農網 – 孫騰浩原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!

當Java 1.5引入註解,企業開發者對簡化EJB和其他企業產品開發抱持很大期望。可以看同一時期的一篇文章用EJB 3.0簡化企業Java開發。

然而從那時起,Java企業使用註解出現一些無法預料的後果和副作用,有些甚至到今天都沒有被注意到。幸運的是,並非所有的副作用都沒有被注意到,來看一些例子,在StackOverflow標題為“Why Java Annotations?”有很多有價值的評論,“Are Annotations Bad?”這篇文章有很棒的觀點,還有“Magics Is Evil”,“Annotations…Good, Bad or Worse?”。

Java中萬惡的註解

並非所有的註解都相同

儘管上面許多討論都包含有價值的觀點,但並不是所有註解都是相同的。

這裡有兩類註解,差別在於他們是否在運行期影響程式。首先,說一下無害的一類,它們並不會在運行期間對程式碼產生任何影響;另一種是有害的一類,它們會修改運行期行為。無害的註解包括@Deprecated, @Override, @SuppressWarnings, 等等。有害的註解包括@Entity, @Table, @PostConstruct, @ApplicationScoped,等等。

在無害的註解中存在一小部分註解,它們非常實用。有一些提供在編譯期間(靜態檢查)捕獲錯誤或提供安全保障。一些實用的註解包括:@Override, @NonNull/@Nullable 來自(Checker Framework), 等等。

為什麼有害的註解不好?

我們定義了一些有害的註解,為什麼要避免使用它們?

想像一個標準的Java Data類別擁有@PostConstruct方法。這個註解表示所標註的方法應該在物件創建好之後被呼叫。這個功能並不是由JVM處理,所以Date類別隱含取得未知的框架和容器,而自身語意並沒有做任何事。如果這些程式碼沒有運行在任何容器中,而只是運行在JVM中呢?這個註解大大降低了這個類別的重用性。另外對於任何使用Date的地方進行單元測試就變成了噩夢,因為你必須確保每次都正確綁定post-construction,要模擬一個相容的容器。這就有點可笑了,一個Date類別需要一個容器來運行,但這確實是有害的註解對類別、方法和參數的影響。

無可否認,業務邏輯往往複雜,需要更多依賴和關係,而不僅僅是一個簡單的Date類別。然而沒有理由在一個類別中明確或隱式地添加不必要的依賴或約束,有害的註解就是:依賴和約束。

企業陷阱

不幸的是有害的聲明在Java Enterprise 5大規模合法化。為了修正早期企業API的易用性問題,註解用來隱藏系統中冗餘的和難用的部分。新的JEE 5被稱讚為”輕量級”和”簡單”,表面上看起來是這樣。但是一個微小的,同時也是至關重要的誤用蔓延開來。

@Stateless
public class DocumentRepository {
   public Document getDocument(String title) {
      ...
   }
   ...
}
登入後複製


如果想要取得一個Stateless EJB,「只需要」在類別上宣告@Stateless註解。確實,編寫這個類別只需要只一點動作,但是請注意這個類別中有害的註解綁定了幾百頁的說明文檔,而且只能在百萬位元組的應用伺服器(Application Server)上運行。這又怎麼能稱的上是」輕量級」呢。所以,這個註解只是真正需要寫的Java程式碼的佔位符而已,程式碼仍需要以某種形式存在。現在只不過是隱藏在註解之下。

不幸的是,這種變通方案稱為一種模式,現在有害的註解廣泛分佈:JPA, CDI, Common Annotations, JAXB 等等。

有害的註解有時會出現在錯誤的地點

因為註解通常作為開發環境,有時有害的註解被當作單一職責原則(Single Responsibility Principle)或關注點分離(Separation of Concerns)的最佳實踐。

讓我們來考慮一下下面這個CDI範例:

@ApplicationScoped
public class DocumentFormatter {
   ...
}
登入後複製


上面的註解描述這個類別應該是一個CDI Bean,意味著它應該只能由CDI實例化,並確保每個應用程式中只有一個實例。

這些資訊並不屬於這個類別。這個服務在功能上(無論什麼方式)並不會對它在目前應用中的作用產生影響。這裡有兩個明顯的關注點。

一個JPA的簡單例子:

@Entity
@Table("PERSON")
public class Person {
   ...
}
登入後複製


问题在于这种类往往是”领域对象(domain objects)”,它们直接将领域模型持久化。更糟的是,数据传送对象(DTO)用来在对象之间传送数据,使得整个构造变得脆弱,因为对象间耦合过于紧密。不管怎样,这是一种错误的方式。

所有的这些附加的功能和(或)信息应该从这些类中分离出来,但是它们却悄悄混在一起,因为它们”只不过”是注解。

有害的注解有时蔓延

注解有时会传染其他对象。回顾上面那个CDI Bean。每个使用它的对象,每个依赖它的对象现在都拥有一个CDI注解,否则依赖关系树就不会构建成功。

@Entity注解也一样。因为对象之间的关系,其他对象也通过注解持久化,很快所有的持久化对象都会有这个注解。我们无法使用原生的第三方对象(除非序列化或包装它们),我们无法使用其他持久化机制(比如用NoSQL DB存放对象)。

这些注解使得这些对象无法复用。它们只能在一个严格的、受控制的、不透明的环境中使用,不能和任何东西整合。

有什么替代品?

是XML吗?当然不是,至少对于上面的例子来说不是。

Spring框架使用配置来管理对象,因此可以用XML当做配置文件。然而,是否某个依赖需要在运行期改变,而不通过重新编译?如果不需要,那么很难说配置应该用另一门语言来表示,尤其重构困难、测试困难、管理需要特殊工具。

真正的替代品当然是好的Java代码,正确封装并解耦的。是的,用代码来管理对象,尽管有时被当做样板(boilerplate),但并不算糟糕。它带来一些好处,比如让代码可读、可调试、可重构。只有那些长片的、复杂的、冗余的样板是糟糕的,比如“关于EJB 2.0”。但是解决方案并不是摆脱所有的样板或用另一种语言隐藏样板,而是简单干净的架构,直接而不多余的信息,简单并合适的方式来面向对象。

这也适用于JPA、Spring和其他东西。误用注解来表示功能会发生Stcakoverflow上这个问题“Arguments Against Annotations”,为什么不用已有的工具呢:比如Java语言本身和编译器,来解决这类问题,面向对象和软件最佳实践。

总结

如果注解在代码运行期加上了额外功能和约束,那它是有害的。这很糟糕,因为它隐藏了类或方法的切面,使之难懂、难复用、难重构、难测试。

不幸的是Java Enterprise不理睬Java开发者社区中发对注解的声音。所以企业级Java和其他”官方”框架更不可能重视这类问题。

至少我们可以持续关注有害的注解,如果可能尽量避免使用,编写新的框架和软件替换掉注解,不会出现有害注解所带来的问题。

 以上就是Java中万恶的注解 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java 中的完美數 Java 中的完美數 Aug 30, 2024 pm 04:28 PM

Java 完美數指南。這裡我們討論定義,如何在 Java 中檢查完美數?

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。這裡我們透過範例討論簡介、如何使用 weka java、平台類型和優點。

Java 中的史密斯數 Java 中的史密斯數 Aug 30, 2024 pm 04:28 PM

Java 史密斯數指南。這裡我們討論定義,如何在Java中檢查史密斯號?帶有程式碼實現的範例。

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

Java 中的時間戳至今 Java 中的時間戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的時間戳記到日期指南。這裡我們也結合範例討論了介紹以及如何在java中將時間戳記轉換為日期。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

如何在Spring Tool Suite中運行第一個春季啟動應用程序? 如何在Spring Tool Suite中運行第一個春季啟動應用程序? Feb 07, 2025 pm 12:11 PM

Spring Boot簡化了可靠,可擴展和生產就緒的Java應用的創建,從而徹底改變了Java開發。 它的“慣例慣例”方法(春季生態系統固有的慣例),最小化手動設置

See all articles