Home >Java >javaTutorial >The most evil annotations in Java
This article was originally translated by Sun Tenghao from MaNong.com. Please read the reprint requirements at the end of the article for reprinting. Welcome to participate in our paid contribution plan!
When Java 1.5 introduced annotations, enterprise developers had great expectations to simplify the development of EJB and other enterprise products. You can take a look at an article from the same period Simplifying Enterprise Java Development with EJB 3.0.
However, since then, the use of annotations in Java enterprises has had some unforeseen consequences and side effects, some of which have not even been noticed today. Fortunately, not all side effects go unnoticed. To look at some examples, there are many valuable comments on StackOverflow titled “Why Java Annotations?” and this article “Are Annotations Bad?” makes a great point. , and “Magics Is Evil,” “Annotations…Good, Bad or Worse?”
While many of the discussions above contain valuable points, not all annotations are the same Are the same.
There are two types of annotations, the difference lies in whether they affect the program during runtime. First, let’s talk about the harmless ones, which will not have any impact on the code at runtime; the other is the harmful ones, which will modify the runtime behavior. Harmless annotations include @Deprecated, @Override, @SuppressWarnings, etc. Harmful annotations include @Entity, @Table, @PostConstruct, @ApplicationScoped, etc.
There are a small number of annotations among the harmless annotations that are very useful. Some provide catching errors during compilation (static checking) or provide safety guarantees. Some useful annotations include: @Override, @NonNull/@Nullable from (Checker Framework), etc.
We have defined some harmful annotations, why should we avoid using them?
Imagine a standard Java Data class with a @PostConstruct method. This annotation indicates that the annotated method should be called after the object is created. This functionality is not handled by the JVM, so the Date class implicitly obtains unknown frames and containers without doing anything semantically. What if this code does not run in any container, but only in the JVM? This annotation greatly reduces the reusability of this class. In addition, unit testing anywhere that uses Date becomes a nightmare, because you have to ensure that post-construction is bound correctly every time, and a compatible container must be simulated. This is a bit ridiculous, a Date class needs a container to run, but this is indeed the harmful impact of annotations on classes, methods and parameters.
It is undeniable that business logic is often complex and requires more dependencies and relationships than just a simple Date class. However, there is no reason to explicitly or implicitly add unnecessary dependencies or constraints to a class. Harmful annotations are just that: dependencies and constraints.
Unfortunately harmful claims have been legitimized in Java Enterprise 5 at scale. To correct early usability issues with enterprise APIs, annotations were used to hide redundant and difficult-to-use parts of the system. The new JEE 5 has been praised as "lightweight" and "simple", and it seems so on the surface. But a tiny, yet crucial misuse spread.
@Stateless public class DocumentRepository { public Document getDocument(String title) { ... } ... }
If you want to obtain a Stateless EJB, you "just need" to declare the @Stateless annotation on the class. Indeed, writing this class requires only a few clicks, but please note that the harmful annotations in this class are tied to hundreds of pages of documentation and will only run on a megabyte Application Server. How can this be called "lightweight"? Therefore, this annotation is just a placeholder for the Java code that actually needs to be written, and the code still needs to exist in some form. Now it's just hidden under annotations.
Unfortunately, this workaround is known as a pattern and harmful annotations are now widely distributed: JPA, CDI, Common Annotations, JAXB, etc.
Because annotations are often used as a development environment, sometimes harmful annotations are treated as Single Responsibility Principle (Single Responsibility Principle) or Best practices for Separation of Concerns.
Let us consider the following CDI example:
@ApplicationScoped public class DocumentFormatter { ... }
The above annotation describes that this class should be a CDI Bean, meaning that it should only Instantiated by CDI and ensuring only one instance per application.
This information does not belong to this category. This service has no functional impact (in any way) on its role in the current application. There are two obvious concerns here.
A simple example of 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)!