反对注释的论据

我的团队正在转向Spring 3.0,并且有些人希望开始将所有内容都转移到注释中。 当我看到一个有这样的方法的类时,我的内心(代码味道)只是一个非常糟糕的感觉(仅仅是一个例子 – 并不是所有的真实注释)

@Transaction @Method("GET") @PathElement("time") @PathElement("date") @Autowired @Secure("ROLE_ADMIN") public void manage(@Qualifier('time')int time) { ... } 

我只是在时代的背后,还是这似乎对任何人都是一个可怕的想法? 而不是使用面向对象的概念,如inheritance和多态现在,一切都是按照惯例或通过注释。 我只是不喜欢它。 不得不重新编译所有的代码来改变IMOconfiguration的东西似乎是错误的。 但似乎是一切(特别是spring)的方式。 我应该只是“克服它”,或者我应该推回来,尽量保持我们的代码尽可能免费注释?

其实我认为你的内心不好的感觉更多的是像这样的混合configuration与代码的注释。

就我个人而言,我觉得和你一样,我宁愿在代码库本身和外部的Spring XML上下文文件之外留下configuration(比如事务定义,path元素,控制器应该映射到的URL等等) 。

我认为,虽然这里的正确方法归结为意见,你更喜欢哪种方法 – 我预测一半的社群会同意注释方法,另一半会同意外部configuration方法。

也许你有一个遍布代码的冗余注释的问题。 使用注释可以replace多余的注释,并且您的注释至less是干的。

从spring博客:

 @Service @Scope("request") @Transactional(rollbackFor=Exception.class) @Retention(RetentionPolicy.RUNTIME) public @interface MyService { } @MyService public class RewardsService { … } 

由于Java的演变速度如此之慢,人们正在将更多的语言中缺less的function添加到注释中。 这是一件好事,Java可以以某种forms扩展 ,这是一件坏事,因为大多数注释是一些解决方法,并增加了复杂性。

我最初也对注释持怀疑态度,但看到他们在使用,他们可以是一个伟大的事情。 他们也可以被过度使用。

关于注释的主要事情是它们是静态的。 他们不能在运行时更改。 任何其他configuration方法(xml,代码中的自我描述,不pipe)都不会因此而受到影响。 我曾经在这里看到过人们在使用Spring时遇到了一些问题,例如在注入testingconfiguration的testing环境方面,不得不下降到XML来完成。

XML不是多态的,也不是inheritance的,或者其他任何东西,所以在这个意义上,它不是一个倒退。

注释的优点是它可以给你更多的静态检查你的configuration,并可以避免在XMLconfiguration(基本上保持干燥)的冗长和协调困难很多。

就像XML一样,注释可以被过度使用。 主要的是要平衡各自的需求和优势。 注释,在一定程度上,他们给你较less的冗长和DRYer代码,是一个杠杆的工具。

编辑:关于一个注释replace一个接口或抽象类的评论,我认为在框架边界可以是合理的。 在一个旨在被数百人使用的框架中,如果不是成千上万的项目,拥有一个接口或基类可以真正地压缩事物(特别是基类,但是如果你可以用注释来做,那么没有理由你不能做它与一个常规的界面。

考虑一下JUnit4。 之前,你必须扩展一个基类,它有一个设置和拆卸方法。 就我而言,如果这些是在一个接口或基类上,那并不重要。 现在我有一个完全独立的项目,它有自己的inheritance层次,他们都必须尊重这个方法。 首先,他们不能有自己的冲突的方法名称(在testing框架中没有什么大不了的,但你明白我的观点)。 其次,所有的方法都要调用超级链,因为所有的方法都必须耦合。

现在使用JUnit4,可以在层次结构中的不同类中使用不同的@Before方法,它们可以相互独立。 没有注释,没有同样干的方法来实现这一点。

从JUnit开发者的angular度来看,这是一场灾难。 最好有一个定义的types,你可以调用setUp和teardown。 但是框架开发者的方便性并不存在,为了框架用户的方便而存在。

如果你的代码不需要关心types,那么所有这些都适用(也就是说,在你的例子中,没有什么会真正使用Controllertypes的)。 那么你甚至可以说,实现框架的界面比注释更为漏洞。

但是,如果您将要编写代码来在您自己的项目中读取该注释,请远离运行。

检查这些答案类似的问题

与xmlconfiguration文件相比,Annotations(非编译器)的优点/缺点是什么?

Xmlconfiguration与基于注解的configuration

基本上归结为:同时使用。 他们两人都有那些用具。 不要将注释用于应该保持可configuration的状态,而不用重新编译所有的东西(特别是你的用户应该能够configuration而不需要你重新编译的东西)

如果使用度量,我认为注释是很好的。 像@WebService这样的注释在部署和运行时会做很多工作,但是它们不会影响类。 @Cachexxx或@Transactional通过创build代理和大量的工件来清楚地干扰,但是我认为它们在控制之下。

当使用Hibernate或JPA注释和CDI时,事情开始变得混乱。 注释增长很多。

IMO @Service和@Repository在您的应用程序代码中是Spring的干扰。 他们使你的应用程序的Spring依赖,只有spring使用。

Spring Data Graph的例子是另一回事。 @NodeEntity,例如,在构build时向类中添加方法来保存域对象。 除非你有Eclipse和Spring插件,否则会出错,因为这些方法在源代码中不存在。

对象附近的configuration有其好处,但也是单个configuration点。 注释对于度量来说是很好的,但是对于所有的事情来说它们都不是很好,而且当注释行和源代码行一样多的时候,注释是非常糟糕的。

我认为spring走的路是错误的; 主要是因为在某些情况下,没有别的办法做这样有趣的事情。 就好像Spring想要做极限编码,同时又把开发人员locking到Spring框架中。 Java语言可能需要另一种方式来做一些事情。

注意必须谨慎使用。 他们对一些人有好处,但不是全部。 至lessxmlconfiguration方法保持configuration在一个文件(或多个),而不是遍布整个地方。 这将引入(因为我喜欢称之为)蹩脚的代码组织。 如果分布在数百个文件中,您将永远无法看到configuration的完整画面。

我个人觉得注释已经占据了太多的位置,并且从原来的和超级有用的目的(例如,指示重写的方法之类的小事情)被炸成这个疯狂的元编程工具。 我不觉得JAva机制足够强大,可以处理每个方法之前的注释集群。 比如说,我现在正在和JUnit注解作斗争,因为它们以我不喜欢的方式来限制我

这就是说,根据我的经验,基于XML的configuration也不是很好。 所以引用南方公园,你select了一个巨大的冲洗和三明治。

我认为,你必须做出的主要决定是,你是否更习惯于configurationspring的本地化(即维护两个文件而不是一个文件),以及是否使用从注释中受益的工具或IDE插件。 另一个重要的问题是,将使用或维护您的代码的开发人员是否真正理解注释。

像许多事情一样,有利弊。 在我看来,一些注释是好的,虽然有时感觉有一种倾向于过度使用注释时,一个简单的旧function调用方法可能是优越的,并作为一个整体,这可能会无意中增加认知负荷,因为他们增加“做东西”的方法。

让我解释。 例如,我很高兴你提到@Transactional注释。 大多数Spring开发人员可能会了解和使用@Transactional。 但有多less开发人员知道@Transactional是如何工作的? 他们是否知道如何创build和pipe理交易而不使用@Transactional注解? 使用@Transactional可以使我在大多数情况下更容易使用事务,但在特殊情况下,当我需要对事务进行更精细的控制时,它会隐藏我的这些细节。 所以从某种意义上说,这是一把双刃剑。

另一个例子是Springconfiguration类中的@Profile。 在一般情况下,可以更容易地指定您希望加载一个Spring组件的configuration文件。但是,如果您需要更强大的逻辑,而不仅仅是指定要加载组件的configuration文件列表,则必须获取环境对象自己并写一个函数来做到这一点。 同样,大多数Spring开发人员可能会熟悉@Profile,但是这样做的副作用是他们不太熟悉它的工作细节,比如Environment.acceptsProfiles(String … profiles)函数。

最后,当注释不起作用时,理解为什么,而不能只在注释上放置一个断点。 (例如,如果您在configuration中忘记了@EnableTransactionManagement,会发生什么情况?)您必须find注释处理器并对其进行debugging。 使用函数调用方法,当然可以在函数中放置一个断点。

我认为这在一定程度上取决于你什么时候开始编程。 我个人认为他们是可怕的。 主要是因为他们有一些准意义上的“意义”,除非你意识到有关的注释,否则你不会理解。 因此,他们自己形成一种新的编程语言,并使您远离POJO。 与(比较)简单的旧的OO代码相比。 第二个原因 – 他们可以阻止编译器为你做你的工作。 如果我有一个庞大的代码库,想要重构一些东西或者重命名一些我最喜欢的编译器来抛出所有需要改变的东西,或者尽可能多的东西。 注释应该是这样的。 注释。 不是你的代码行为的核心。 它们的devise原本是在编译时可以省略,它告诉你所有你需要知道的东西。

是的,我知道XMLconfiguration遭受同样的方式。 这并不会让事情变得更糟,也同样糟糕。 至less我可以假装忽略这一点 – 它不会在每一个方法或参数声明中面对我。

鉴于我的select,我实际上更喜欢可怕的旧的J2EE远程/家庭接口等(最初由Spring人批评),至less可以让我了解发生了什么,而不必研究@CoolAidFrameworkThingy及其弱点。 框架人员面临的一个问题是,他们需要将你与他们的框架联系起来,以使整个企业在财务上可行。 这与devise一个框架相悖(也就是说,它是独立的,可以从代码中移除)。

不幸的是,注释是时髦的。 所以,如果你没有进入代码评论/标准等等,那么你将很难阻止你的团队使用它们(也是不合时宜的!)

我读到Stroustup在C ++中留下了注释,因为他担心它们会被误用。 有时候事情往往走错了几十年,但是你可以希望事情能够及时到达。

注释经常引入依赖关系不属于的地方。

我有一个巧合的类,它具有类似于RDBMS模式中表的属性的属性。 这个类是根据这个映射创build的。 class级和class表之间显然有关系,但是我很乐意让class级免于任何声明这种关系的元数据。 这个类是否在一个完全不同的系统中引用一个表和它的列是正确的? 我当然不反对把两者联系在一起的外部元数据,而让每个元数据彼此之间没有了解。 我得到了什么? 这不像源代码中的元数据提供types安全性或映射一致性。 任何可以分析JPA注释的validation工具都可以很好地分析hibernate映射文件。 注释没有帮助。

在一个合同中,我创build了一个Maven模块,其中包含一个来自现有软件包的接口的实现包。 不幸的是,这个新的软件包是整体构build中的许多目录之一。 我把它看作与其他代码分开的东西。 尽pipe如此,该团队正在使用类path扫描,所以我不得不使用注释为了让我的组件连接到系统。 在这里我不想集中configuration; 我只是想要外部configuration。 XMLconfiguration并不完美,因为它将依赖接线与组件实例相结合。 鉴于Rod Johnson不相信基于组件的开发,这是公平的。 尽pipe如此,我再次觉得注释并没有帮助我。

让我们把这个与不打扰我的东西进行对比:TestNG和JUnittesting。 我在这里使用注释,因为我知道我正在使用TestNG或JUnit编写此testing。 如果我replace为另一个,我知道我将不得不进行一个昂贵的过渡,这将变得接近重写testing。

无论出于何种原因,我接受TestNG,JUnit,QUnit,unittest和NUnit拥有我的testing类。 在任何情况下,JPA或Hibernate都不会拥有那些碰巧映射到表的域类。 Spring在任何情况下都不拥有我的服务。 我控制我的逻辑和物理包装,以隔离依赖任何一个单位。 我想确保离开一个不会因为它留下的所有依赖而使我残废。 说再见总是比离开容易。 在某个时候,离开是必要的。