为什么Java正在死去? - Komal


微服务无所不在的浪潮席卷了我们:

  • 易于扩展
  • 高可用性
  • 无需担心并发和多线程的简化代码库
  • 集装箱化带来了可移植性

所有这些因素促使我们质疑Java(更具体地说是JVM)的功效,更不用说Java最臭名昭著的框架Spring了。
有时,人们沉浸在诸如Kubernetes这样的技术中,感觉Java的时代已经过去,Java在容器和微服务生态系统中的表现还不理想(这是软件可扩展性和高可用性的关键)。但是,作为曾经坚定支持Java的人-尽管一直受到Python等语言(现在已经成为我的首选语言)的简单性和优雅的影响,但我仍然继续为Java不可否认的某些领域保留一席之地优点。
例如,我很清楚Java的强大线程功能,在我的职业生涯早期就将它们直接用于关键银行应用程序。虽然将编译语言的性能指标与脚本语言的性能指标进行比较是不公平的,但Java坚如磐石的性能却是无与伦比的。
但是在水平可伸缩性和微服务体系结构的世界中,语言本身固有的高性能不重要了,因为人们可以简单地产生更多的容器worker来获得出色的性能。Python等脚本语言可以在容器领域中即时放大或缩小,性能相比Java已经毫不逊色了。
我完全地确信Java已经完成了使命(至少在微服务领域如此)。在我的新工作中,这些信念仅得到进一步加强,我痛苦地意识到Java语言变得多么令人讨厌,烦躁和令人费解:部分原因是由于Spring等过时的仪式框架。
 
Java和Spring的仪式
让我们从臭名昭著的Spring框架开始。
与五年前相比,Spring是如此庞大且令人费解,充斥着无穷无尽的注释,这些注释使开发人员在每次需要完成工作时都只能依靠教程或示例代码。仔细阅读Spring自己的详尽文档既可能是艰巨的任务。
实际上,我最喜欢的是像Spring这样的框架,而不是Java本身。Spring采用了一种已经很礼貌的语言,用单行注释和看似简化的包装器对其进行掩盖,从而加剧了这个问题,这些包装器最终召唤出了您通常不需要的类的调用和实例化的狂欢。正如任何开发人员都会同意的那样,语言的控制,命令和透明性对于有效的软件开发至关重要。简而言之,作为一名开发人员,您想准确地了解代码中发生了什么以及执行了哪些例程-至少是在较高层次上。但是Spring在这方面痛苦地阻止了您。
如果您必须在类的顶部放置六个注释,而每个注释都在做自己的事情,并且在Spring上下文的网格中错综复杂地相互联系,那么您将处于一片模糊的境地。这不仅是Spring是这样。
以Lombok库为例。这是其首页上宣传的:
“ Project Lombok是一个Java库,它会自动插入您的编辑器和构建工具中,从而为您的Java增光添彩。永远不要再编写另一个getter或equals方法,带有一个注释的类将具有功能齐全的生成器,自动执行日志记录变量等等。”
压缩Java代码令人沮丧,并且需要痛苦地面对这种压缩了的语言工作,这种方式带来不了任何好处。
Java应该简单地停止尝试与脚本语言的简洁性相匹配。首先,这牺牲了Java代码的一致性:想象回到Java只是发现所有的getter和setter都消失了(根据我们曾经学过的知识这些属性方法对于Spring autowire自动装配很重要),现在已被单行注释替换@NoArgsConstructor。逻辑上的一致性在哪里?
其次,它增加了已经令人费解的抽象数组。例如,在这里,Spring可以在后台设置自动装配(bean注入),这是可以理解的,但是Lombok在应用程序上下文中位于何处,以及如何在两者之间协调消息传递?
如果我的每个类都有六个注释,那么这些注释还实例化了多少其他例程或类来完成这一简化的工作?没有真正的开发人员会希望将所有这些额外的代码潜伏在角落。可悲的是,这是三年后我遇到的那种Java代码。没有一件事情发生改变。实际上,即使发生的微小变化也只会使情况变得更糟。
 
Java仍将重点放在愚蠢的规则上,这些规则规定了应使用的类名称,应使用的包以及变量是私有的还是受保护的。说真的,谁在乎?
相反,“我们都是成年人”实际上是Python对语言中缺少访问说明符的官方回应。这种嘲讽而引人入胜的单行回应立刻引起了我的共鸣。最终,它使我经常觉得是荒谬且不必要的概念更为理智。
 
保持简单,愚蠢
如果您在软件行业中不停地听到一件事,那就是KISS的首字母缩写:保持简单,愚蠢。如果Java要生存,这是需要认真考虑的事情。
如今,微服务模式已在软件行业中几乎普及。甚至许多运行旧版应用程序的组织也越来越多地替换其旧的整体,以简化设计并提高可伸缩性。对于程序员而言,这意味着将其庞大的代码库或复杂的业务逻辑分解为更简单,简洁的功能-一种无需在代码中进行状态管理的范例,从而免除了并发问题和多线程噩梦。
归根结底,所有服务,无论是某种形式或形式,都只处理某种格式(JSON或XML)的数据,然后将它们传递到诸如Kafka的消息总线上以进行进一步处理。甚至在一个可笑的简单的设置像这样,Java和Spring继续秉承其隆重代码语法以及倒退到不合时宜的修辞。Application Contexts、错综复杂bean injections、autowiring、 POJO mappers、消耗内存JVM和臭名昭著的class loader-所有这些都是毫无意义的痛苦来处理。

黑客新闻讨论
我是一个刻板的Java开发人员,即使我过去使用过许多其他语言。我认为作者错过了Java设计的重点。当您需要良好的性能而不是绝对的最佳性能时,它应该是更友好,更安全,更通用的C ++。因此,我认为这是最好的选择。
总的来说,这篇文章是来自那些从未使用过Java或只做过一些Spring教程,对语言的复杂性感到沮丧并转向其他内容的开发人员的普遍抱怨。
Java是最快的流行语言之一,JVM是一个很棒的运行时,而不是臃肿肿的运行时。看看V8有多庞大,与Java相比,JS标准库仍然是垃圾。JVM可能是目前最好的运行时,这也是Java为什么如此流行的很大一部分。
JVM具有强大而快速的FFI,配置文件,指标,多个世界一流的GC,它非常稳定,它可以毫无问题地处理带有数百兆代码的项目。
 
不是Java语言本身搞砸了,而是框架和库中注释的大量使用,使构建应用程序一次写入就永远无法调试问题。当您在不熟悉的现有代码库上开始工作时,真正的问题就开始了。对Spring的深入了解是即使是很小的工作的绝对要求。
 
我认为Java中的许多“最佳实践”,尤其是那些来自“有效Java”的实践,对于大多数开发人员来说都是偏执狂,只会制造出比他们解决的问题更多的问题。
 
像Java那样死胜于像Rust那样活着,虽然我不喜欢典型的Java框架的复杂性,并且由于这个原因七年来没有做过任何Java,但称Java死了是因为“每个人都在使用微服务”以及“当您可以运行更多代码容器worker时为什么要为性能而烦恼”有点荒谬。因为在高流量服务上使用Python,因此部署的计算机数量要多出3倍。
 
我的公司有一个庞大的,过度设计的整体,我们将其分解为更明智的微服务,并且该架构现在更清洁,更易于维护,扩展等。不过,我同意,预先设计微服务通常是过早的优化,因为您最初对服务之间的界限放在何处的猜测很可能是错误的。
整体和微服务都是使用Spring Boot用Java编写的,但是我同意作者的观点,即Spring特别导致运行时代码,这对于开发人员来说是很难真正理解的。我很好奇,从长远来看,用Go之类的语言编写未来的微服务是否会更可维护。
 
我不同意Go是Java Microservices的良好替代品。我建议您研究一下Quarkus(https://quarkus.io)之类的东西,如果Java对某人来说很冗长,她/他可以使用Kotlin(https://kotlinlang.org),它可以与Java 100%互操作但干净,现代,务实。使用Quarkus,您有一个非常强大但简单的框架,如果您需要非常快速的启动和非常低的内存消耗,则可以将其开箱即用(GraalVM)编译为单个二进制文件。
Go宣扬了简单性,但是当有人必须在更大的项目中使用它时,就会很麻烦。您没有任何函数性的编程功能,例如过滤器,映射,查找来操纵集合。非null(nil)安全性或带接口的魔术{}确实使它变得不安全,特别是与Kotlin,Swift等现代语言相比,这种语言本身就具有可空性的概念。依赖注入支持非常有限。我认为Go是被炒作的,如上所述,您今天通过GraalVM以及Java,Kotlin也具有出色的单一二进制功能。
 
如果您不喜欢Spring,请不要使用Spring!
如果您不喜欢注释,则不要使用注释!
然后,您无需撰写煽动性的头条新闻...
 
Spring是完全不能容忍的。如果注释错误,则根本无法调试该过程。我急切地寻找能够深入了解在运行时autowire了哪些对象的工具。
 
用调试器观察运行时行为很简单。如果建议使用构造函数注入,则只需在构造函数中添加一个断点,然后在调试器中启动应用程序即可。
 
对于他所说的关于Spring的一切表示同意,但感到惊讶的是他主张使用Python作为替代方案,而不是Go。在微服务环境中,它似乎是Java的更直接替代。比Python性能更高,易于部署,并提供了大量的静态类型检查。
 
我已经用Java编写了很多微服务,并且最近开始使用非常相似的体系结构(但是使用Go)启动了一个新项目。
我有机会反省了我对Go的满意之处以及Java遗漏的内容。

  • *由于很多原因,我都不喜欢Java语言,尽管两者都不是完美的。
  • *我非常喜欢使用二进制而不是字节码对象,因此Go在这里要好得多。
  • * JVM是与代码关联的非常大的运行时,并且启动时间仍比Go(或一年前)更长。这会使Java CLI工具变慢,并且出色的CLI可以帮助制作出出色的产品。
  • *我更喜欢Go的标准库方法;易于使用的生产就绪“傻瓜”实现,而不是抽象的“引用”实现。
  • *我特别不喜欢Go使用大写形式将字段和变量标记为公共,因为我发现类型和变量名称之间的冲突确实很常见。
  • * Go的“惯用”实践比Java聪明得多。我已经停止使用Java中的getter和setter方法,并放弃了其他一些抽象的“最佳实践”(工厂工厂!所以我想我不太喜欢惯用的Java。我感到自己并不孤单。
  • *总的来说,我喜欢Go的感觉,就像它是Unix工具一样,在Java中,Java总是感觉像他们在试图重写世界。考虑到Java的来源,这种方法对我来说总是很奇怪。
  • *我真的很讨厌Java中的“框架中的一切”方法,并在几年前放弃了它。特别是Java EE,Wildfly,Spring,JPA等。它们都增加了巨大的重量和复杂性,而没有实现任何实质性的收益。并不是一定要这样,而且我认为情况正在改善,但是有许多Java库只是非常复杂和抽象,没有充分的理由。

因此对于我的用例来说,Go显然是正确的选择,但是在Go中工作12个月后,我对Java语言有很多想念的地方。我对Java AOT的东西寄予了很高的期望,但它似乎停滞了(也许我错过了一些东西)。