深入介绍Spring框架及故障排除

目录
  • Spring的缺点
    • 不可理解性
    • 按注释编程
    • 故障排除
    • 为什么是Spring IoC?
    • 选择
    • 其他人在说什么
  • 结论

前言:

曾几何时,Spring框架提供了比J2EE更轻量级和更灵活的解决方案。即使在2013年左右,我也很高兴详细了解当时的新款Spring 4。如今,7年后,当我看到春天的时候,我感到一阵恐慌。注释和@ComponentScan已经用更好的东西取代了XML,这需要一个可视化工具来理解您的系统。Spring变成了一头不断生长(和变化)的水螅。我接手并试图理解他人编写的Spring应用程序,这让我很痛苦。最后但并非最不重要的一点是,Clojure教会了我代码可以/应该是多么简单。那么,我对Spring的主要问题是什么?

(为了明确起见,当我说Spring时,我指的主要是Spring控制反转(IoC)及其依赖注入子集。)

Spring的缺点

不可理解性

系统构成的黑箱:

注释和组件扫描对于让应用程序快速启动和运行来说非常棒。但当您试图理解所述应用程序时,它们是一场噩梦。由于@Configurations@bean来自您的代码库、公司库中的任何地方,并且可能来自±100MB的Spring依赖项(真实情况),因此不可能清楚地了解应用程序的结构和配置。您基本上需要一次又一次地为Spring和所有库进行RTFM,以便记住可能需要的所有内容,并通读整个代码库。诸如IntelliJ SpringBeans视图之类的工具可能会有所帮助(如果您能够正常工作的话)。

在这里,我完全同意Python的禅宗“显式优于隐式”

在一个典型的Clojure项目中,我转到核心/主名称空间,其中主函数启动服务器并为其提供一个处理函数,可能(手动)使用一些中间件包装,可能在内部使用库进行路由。我还可能阅读配置并传递它。我可以轻松地单击浏览代码,并查看代码中的具体部分以及它们是如何协同工作的。即使是在cljdoc这样的大型系统中。org有一个主功能启动服务器(这里是Integrant),并为其提供配置和“系统定义”(类似于Springbean树)。一切都是明确的和可导航的。

按注释编程

能够向方法和类添加元数据非常棒。我对@GetMapping(“/”)之类的东西没有任何反对意见。但它通常被用来绕过Java的限制,并通过诸如@Scheduled@Transactional之类的注释实现横切关注点。我曾经是AOP的坚定支持者,由于语言的限制,AOP仍然是Java开发人员不可或缺的工具,但我也意识到它的成本不容忽视。问题是,您无法轻松看到它在做什么(因为它什么都没做,只是数据)。为了给您一个透视图,替换@Transactional Person findPersonInDb(String personId) {..}在Clojure中,我只需要用一个自定义宏来包装它,例如:

(defn find-person-in-db [person-id]
  (transactional
    ...))

实质性的区别是,我可以控制单击导航到宏,并查看它在做什么,因此所有行为都在那里,供我检查和理解。祝你在Spring找到答案!

故障排除

Spring是让很多事情快速起步的好时机——直到某些事情失败或不按预期工作为止。Spring是一个松散耦合的意大利面大球,在我痛苦的经历中,解决它是非常困难的。有很多文档,但我经常找不到我需要的答案。也许官方文件太肤浅,有时依赖于大量已有的知识。搜索互联网有时提供了一个解决方案,有时至少提供了有用的指针,有时提供了误导性/旧信息,有时什么都没有。在进行故障排除时,您需要艰难地通过这个庞大复杂的类,这些类以某种方式协同工作(或应该协同工作),以(似乎)神秘的方式受到类路径上的jar和@Configurations的影响,并希望您能够偶然发现问题的原因。

例如,我花了相当长的时间来理解Spring MVC应用程序中的错误处理是如何工作的。我们有一个@ControllerAdvice(["myapp.endpoint.api"]) myapp.endpoint.api.advice.ErrorHandler调用的ErrorHandler(如果您记得抛出正确的异常类型),@Component myapp.spring.ErrorPagesCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>将container.addErrorPages(new ErrorPage("/error"))错误发送到org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController,然后它将神奇地呈现我们的错误。类路径上的html。我意识到我再也不明白(如果我真的明白的话)它到底是如何工作的了。但要以所需的方式显示错误,无疑是一场斗争。

另一个难点是理解为什么Spring返回404而不是预期的文件。我对它的请求处理有很多来之不易的知识,也许有一天会成为一篇博客文章。

了解@Scheduled jobs实际上是如何调度的,并试图找出为什么一个作业没有按预期运行,以及增加线程池大小以使其不再被较慢的作业停止的神奇调用是什么,这需要几天的时间和多次失败的尝试,我甚至放弃了一次或两次。搜索互联网提供了一些帮助,但肯定远远不够。这是我的一般经验,与Spring有关。

在Spring升级后搜索突然出现的ClassNotFound运行时错误,试图找出哪个Spring JAR有这个类,哪个版本是正确的,或者如何更改配置以停止需要更改…​ 不,谢谢你。

为什么是Spring IoC?

这是一个由两部分组成的问题:为什么是Spring IoC?为什么是Spring?

许多开发人员都知道,Spring是一个成熟且流行的解决方案。它还为几乎所有问题提供了解决方案,这些解决方案通常能够很好地协同工作。您也可以快速开始。另一方面,你可能会争辩说,这是夸大其词,有遗留问题,而且——通过尝试为每个人做每件事——没有做得完美,更小、专注的解决方案可能更好(尽管你需要整合它们)。

为什么选择依赖注入和IoC?您可以从中找到许多原因:使用依赖注入和IoC容器的好处是什么?。

其中一些是:

  • 简化-您的类不需要知道如何创建它们的依赖项(以及它们需要什么)。您可以将实例化和连接类的问题分离出来。
  • 灵活性—您现在可以提供不同的/修饰的实现。因此,在测试中,您可以提供模拟实现,在大型复杂系统中,通过交换新的实现(这部分取决于对接口的编程),您可以使用疑难解答装饰器包装依赖项,从而使其更容易逐步重构。
  • 生命周期控制

查看维基百科上列出的优点和缺点。但它有明显的好处,这并不意味着你应该在任何地方、任何事情上都使用它。记住成本和劣势,做出有根据的决策。

我们通常在Clojure-f.ex中使用依赖注入。接下来是数据库访问库。jdbc要求您将目标数据源传递给每个调用。(这使您可以自由地创建自己的包装器来控制数据源并将其传递给库(如果您愿意的话)在Cognitect AWS API中构造AWS客户端时,可以让它创建默认的底层HTTP客户端,也可以提供自己的客户端,这样就可以覆盖默认的依赖关系。我们甚至有一些人喜欢使用的依赖注入框架,如组件和上下文,而其他经验丰富的开发人员则觉得它们不必要。

选择

当谈到Spring生态系统作为一个整体时,您应该始终应用Alex的合理库原则:不要添加库,直到没有库的痛苦如此之大,您无法忍受没有它的生活(并且在适当探索了其他选择之后)。

当您需要依赖注入时,最好手动组合系统。当前的小型微服务时代与诞生Spring的巨型应用时代截然不同。你可以自己做(为什么不?!)或者使用一种重量轻、重点突出的解决方案来解决更为手动的问题,例如Feather。尽可能喜欢编程配置。Feather仍然是由CDI的@Provides驱动的注释,但至少您在一个“module”类中声明了这些注释,您显式地向Feather注册了这些注释,并显式地向Feather请求所需的实例。在过去,我们使用Guice和手动调用来绑定每个微服务的主类。对于我这个当时经验丰富的Spring用户来说,这似乎很奇怪,也很错误,但我开始理解并欣赏它。甚至还有用于Spring Boot的(实验性)编程配置DSL JaFu(Kotlin,KoFu也有一个)。

据一些人说,Jakarta(Java EE的后代)是Spring生态系统的一个更干净、更小、更好的替代品。您还可以针对特定需求搜索单个解决方案。

我从一位受人尊敬的同事那里听到了Micronaut的好消息,它提供了低开销的DI和AOP、REST客户端/服务器、反应式、断路器等,但它仍然依赖(似乎)类路径扫描进行配置,因此保留了我在Spring中的主要问题。还有反应型Helidon SE,它具有“透明的”的开发经验;纯java应用程序开发,无注释,无依赖注入”与这两个领域相同的领域是Quarkus,但其IoC基于CDI,因此与Spring有相同的问题。Eclipse Vert.X 专注于反应式、事件驱动的应用程序有所不同,但提供了类似的功能(HTTP客户端/服务器、OpenAPI、GraphQL、DB访问、配置、断路器、安全性、度量),并具有编程配置;相反,它不提供依赖注入(但您可能不需要它(尽管总线本身也有问题))。

其他人在说什么

在为本文做研究时,我发现了一些值得分享的经验和观点。

著名的挪威软件架构师Johannes Brodwall写道(2013年,再次遥遥领先于我):

我发现DI容器给我的一些直觉让我改进了设计,但同时,我发现当我移除容器时,解决方案变小了(这很好!),更易于导航和理解,更易于测试。我发现使用容器的成本非常高,这会导致复杂性和大小的增加,以及一致性的降低。

在这场Quora讨论中,有很多好的建议:为什么大多数母语不是Java的程序员似乎对Spring框架持反对意见,他们对Spring框架的哲学有什么不喜欢的?

这方面的问题是:Spring有点破坏了我们从使用Java中获得的简单性好处。Spring谨慎地将复杂性引入到您的项目中,当它工作时,框架表面上很简单,但老实说,有多少人可以解释Spring中发生的事情?调试Spring错误通常看起来像是魔术,需要90%的猜测和模式匹配。Spring通常可以在项目开始时为您节省数周的工作时间,您可能会觉得这些好处是免费的,但事实并非如此。在某些方面,您可以将其与在项目中使用动态编程语言的早期好处进行类比。在项目的初始阶段,它会大大加快您的速度,但复杂性和技术债务会在稍后的阶段打击您。

(当然,他与动态编程语言的比较与我和其他人使用Clojure的经验相反。)

所以他们改成了基于注释的配置,被迫学习,但我还是不喜欢。它仍然不是真正的Java。许多事情都是靠魔法发生的——当它们没有发生时,它们都以同样的方式失败,什么都没有发生。因此,您编辑并重新编译,同样不会发生任何事情。无法判断您是否注释了错误的内容,或者您的注释所说的内容是否与您认为的不同,或者您是否构建了错误的测试。

如果有像样的文档,我还没有看到。我最近研究了RequestParam,它是Spring MVC中普遍存在的一部分,以了解其语义。该页面实际上是无用的。Javadoc是API通信的主要方式;即使有更好的文档存在,这个页面也不会告诉我太多。

关于现代Java应用程序:

Main方法有一行起始Spring和许多类,每个类有5个注释。它们是如何被实例化的,以什么顺序,如果出现问题,如何调试这个过程

下面是我最讨厌Spring的几件事:

它确实会减慢应用程序的启动时间,在运行应用程序之前,你不会知道你的应用程序是否正常工作——在大型商业环境中,这意味着最多需要一个小时的构建和部署时间…然后会得到大量的stacktrace,其中一半与内部spring类有关,一旦您超越了简单的单例布线场景,Spring就会变得非常丑陋、非常脆弱和不可预测,这有助于您起步,但您走得越远,维护的噩梦就越大。

结论

那么,Spring是邪恶的,应该不惜任何代价避免吗?不需要。它使人们能够克服Java的局限性,并提供了许多库来解决实际问题。但它也是巨大、复杂的,而且维护成本很高。对引入库持怀疑态度,考虑多种解决方案,并为您的案例选择最佳解决方案,而不仅仅是Spring解决方案。即使在使用DI和IoC(Spring或其他)时,也要努力实现最大的透明度,并且更喜欢编程配置而不是类路径扫描。如果其他一切都失败了,请编写良好的(java)文档,以便您的继任者能够理解您的系统。

到此这篇关于深入介绍Spring框架及故障排除的文章就介绍到这了,更多相关 Spring框架 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Data JPA框架快速入门之数据持久化存储到数据库

    目录 1 核心概念 CrudRepository接口 PagingAndSortingRepository接口 2 查询方法 3 后续内容介绍 1 核心概念 Spring Data存储库抽象的中心接口是Repository.它把要管理的实体类以及实体类的ID类型作为类型参数.这个接口主要是作为一个标记接口,用来捕捉工作中的类型,并帮助你发现扩展这个接口的接口.CrudRepository接口为被管理的实体类提供复杂的CRUD功能. 自己可以看看Repository的扩展接口以及实现类 IDEA中

  • SpringBoot+Querydsl 框架实现复杂查询解析

    目录 概述 定义查询请求 google-like查询 特定字段的类sql查询 使用Spring Data Querydsl 利用Spring Query DSL实现动态查询 概述 查询功能是在各种应用程序里面都有应用,且非常重要的功能.用户直接使用的查询功能往往是在我们做好的UI界面上进行查询,UI会将查询请求发给查询实现的服务器,或者专门负责实现查询的一个组件.市场上有专门做查询的框架,其中比较出名,应用也比较广泛的是elasticsearch. 定义查询请求 对于服务端来说,前端UI发送过来

  • Spring Data JPA框架的Repository自定义实现详解

    目录 1. Spring Data Repository自定义实现 1.1 自定义特殊repository 1.2 配置类 1.3 解决歧义 1.4 手动装配 1.5 自定义Base Repository 1. Spring Data Repository自定义实现 Spring Data提供了各种选项来创建查询方法,只需少量编码.但是当这些选项不能满足你的需求时,你也可以为资源库方法提供你自己的自定义实现.本节主要介绍如何做到这一点. 1.1 自定义特殊repository 要用自定义的功能实

  • Spring Boot中使用Spring-Retry重试框架的实现

    目录 Maven依赖 注解使用 开启Retry功能 注解@Retryable 注解@Recover 注解@CircuitBreaker RetryTemplate RetryTemplate配置 使用RetryTemplate RetryPolicy BackOffPolicy RetryListener 参考 Spring Retry提供了自动重新调用失败的操作的功能.这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用. 从2.2.0版本开始,重试功能已从Spring Batch中撤出,成

  • springboot]logback日志框架配置教程

    目录 一.application配置文件实现日志配置 日志格式占位符 二.使用logback-spring.xml实现日志配置 2.1.需求 2.2.需求实现 2.3.测试一下 logback既可以通过application配置文件进行日志的配置,又可以通过logback-spring.xml进行日志的配置.通常情况下,使用全局配置文件application.yml或properties进行配置就足够了,如果您的日志输出需求特别复杂而且需求比较个性化,可以考虑使用logback-spring.x

  • Spring Boot Rest常用框架注解详情简介

    目录 开始Spring Boot Rest的先决条件 在Spring Initializer创建Spring Boot项目 Spring Boot注解 @RestController @RequestMapping @RequestParam @PathVariable @RequestBody REST方法的特定注释 @GetMapping @PostMapping和@PutMapping @DeleteMapping 前言: 让我们了解一下Spring Boot Rest框架注释.它是如此简

  • Spring Data JPA框架快速入门之自定义Repository接口

    目录 自定义Repository接口 repository接口定义 使用Repository接口 自定义Repository接口 要定义一个repository接口,你首先需要自定义一个实体类专用的Repository接口.该接口必须扩展 Repository,并将其类型指定为实体类和实体类的 ID 类型. 如果你想为该实体类资源类型开放CRUD方法,请直接继承CrudRepository而不是Repository. repository接口定义 通常,你的repository接口会扩展Repo

  • springboot整合flowable框架入门步骤

    最近工作中有用到工作流的开发,引入了flowable工作流框架,在此记录一下springboot整合flowable工作流框架的过程,以便后续再次使用到时可以做一些参考使用,如果项目中有涉及到流程审批的话,可以使用该框架帮我们实现流程图示化展示的功能,为了快速了解flowable工作流框架的一个使用过程,我们直接步入主题,springboot整合flowable工作流框架的步骤如下: 1.首先创建一个springboot工程,然后引入flowable pom依赖,代码如下: <dependenc

  • 深入介绍Spring框架及故障排除

    目录 Spring的缺点 不可理解性 按注释编程 故障排除 为什么是Spring IoC? 选择 其他人在说什么 结论 前言: 曾几何时,Spring框架提供了比J2EE更轻量级和更灵活的解决方案.即使在2013年左右,我也很高兴详细了解当时的新款Spring 4.如今,7年后,当我看到春天的时候,我感到一阵恐慌.注释和@ComponentScan已经用更好的东西取代了XML,这需要一个可视化工具来理解您的系统.Spring变成了一头不断生长(和变化)的水螅.我接手并试图理解他人编写的Sprin

  • Spring框架七大模块简单介绍

    Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架. Spring框架的7个模块 组成 Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现.每个模块的功能如下: 1核心模块 SpringCore模块是Spring的核心容器,它实现了IOC模式,提供了Spring框架的基础功能.此模块中包含的BeanFactory类是Spring的

  • Spring框架之IOC介绍讲解

    目录 一.对spring的理解 二.spring中ioc的特点 2.1控制反转 2.2注入依赖 方式① set注入 方式② 构造注入 方式③ 自动装配 三.spring与web容器的整合 一.对spring的理解 spring是一个开源框架,它由Rod Johnson 创建.它是为了解决企业应用开发的复杂性而创建的. 目的:解决企业应用开发的复杂性 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 范围:任何Java应用 简单来说,Spring是一个轻量级的控制反转(IoC

  • Spring框架中一个有用的小组件之Spring Retry组件详解

    1.概述 Spring Retry 是Spring框架中的一个组件, 它提供了自动重新调用失败操作的能力.这在错误可能是暂时发生的(如瞬时网络故障)的情况下很有帮助. 在本文中,我们将看到使用Spring Retry的各种方式:注解.RetryTemplate以及回调. 2.Maven依赖 让我们首先将spring-retry依赖项添加到我们的pom.xml文件中: <dependency> <groupId>org.springframework.retry</groupI

  • 图解Spring框架的设计理念与设计模式

    本文主要剖析Spring框架的作者设计Spring框架的骨骼架构的设计理念,有那几个核心组件?为什么需要这些组件?它们又是如何结合在一起构成Spring的骨骼架构?Spring的AOP特性又是如何利用这些基础的骨骼架构来工作的?Spring中又使用了那些设计模式来完成它的这种设计的?它的这种设计理念对我们以后的软件设计有何启示?本文将详细解答这些问题. Spring的骨骼架构 Spring总共有十几个组件,但是真正核心的组件只有几个,下面是Spring框架的总体架构图: 图1.Spring框架的

  • Spring Boot快速搭建Spring框架教程

    Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来. 它是为了解决企业应用开发的复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架.Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情. 然而,Spr

  • 一般故障排除步骤与方法

    一般故障排除 硬盘的分区损坏.当硬盘启动时,会出现"Invalid partition table",而且用系统软盘启动后,用"DIR C:"命令,如果出现"Invalid drive specification",说明硬盘的分区损坏.需要用"FDISK"和"FORMAT"命令重新分区格式化. 24.2.1 硬盘故障排除 l.系统引导文件被破坏的处理 启动计算机时,屏幕出现"Missing Ope

  • JAVA学习之一步步搭建spring框架

    前言 作为一个服务端开发感觉一直挺排斥框架这种东西的,总觉得什么实现逻辑都帮你封装在里面了,你只需要配置这配置那个,出了问题也不知道怎么排查,之前即使写web程序也宁愿使用jetty这样的嵌入式的web server实现,自己写servlet,总感觉从main函数开始都在自己的掌控范围之内,但是这样的方式的确有点原始,也看到各种各样的开源系统使用spring实现web服务,虽然代码总是能够看明白,但是还是不晓得一步步是怎么搭建的,于是抽出一个周末折腾折腾,不搞不知道,原来这玩意能把一个不熟悉的用

  • 简介Java的Spring框架的体系结构以及安装配置

    体系结构 Spring有可能成为所有企业应用程序的一站式服务,然而,Spring是模块化的,使您可以挑选哪些模块是适用的,而不必把在余下的也引入.以下部分给出详细介绍在Spring框架中所有可用的模块. Spring框架提供约20个模块,可以根据应用程序的要求来使用. 核心容器: 核心容器组成的核心,Beans,上下文和表达式语言模块,其细节如下: 核心模块提供了框架的基本组成部分,包括IoC和依赖注入功能. Bean模块提供BeanFactory是工厂模式的经典实现. Context 上下文模

  • Spring框架web项目实战全代码分享

    以下是一个最简单的示例 1.新建一个标准的javaweb项目 2.导入spring所需的一些基本的jar包 3.配置web.xml文件 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/

随机推荐