怎么理解 Spring Boot 的约定优于配置 ?
在传统的 Spring 开发中,大家可能都有过这样的经历:项目还没开始写几行核心业务代码,就已经在各种配置文件中耗费了大量时间。比如,要配置数据库连接,不仅要在 XML 文件里编写冗长的数据源配置,还要处理事务管理、连接池配置等;搭建 Web 项目时,从配置 DispatcherServlet 到配置各种视图解析器,再到处理静态资源路径,每一步都需要小心翼翼地编写配置代码,稍有不慎就可能导致启动失败或功能异常。
二、什么是约定优于配置
“约定优于配置”(Convention over Configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做出决定的数量,获得简单的好处,而又不失灵活性 。简单来说,就是框架通过一些默认的约定,帮我们预先设置好大部分通用的配置,开发者只需在少数不符合约定的情况下,才需要进行额外的配置工作。
举个生活中的例子,我们去餐厅吃饭,一般餐厅都会有默认的餐具摆放方式和点餐流程。如果没有特殊要求,我们就按照餐厅的 “约定” 来用餐,比如使用摆在桌上的筷子、勺子,在菜单上勾选菜品等。只有当我们有特殊需求,比如想要一次性手套代替筷子,或者需要额外的调料时,才需要向服务员提出 “配置” 要求。这就好比在 Spring Boot 开发中,框架已经为我们设定好了项目结构、依赖管理、配置文件加载等方面的约定,大部分情况下我们无需额外配置,只有在个别特殊场景下,才需要手动调整配置。
在 Spring Boot 中,“约定优于配置” 是其核心设计理念之一,它贯穿于整个框架的使用过程中。通过遵循这些约定,开发者能够快速搭建起一个功能完备的 Spring 应用,无需花费大量时间和精力在繁琐的配置工作上,从而将更多的注意力集中在业务逻辑的实现上,极大地提升了开发效率。
三、传统开发与 Spring Boot 对比
(一)传统 Spring 开发的配置痛点
在传统的 Spring 开发模式下,开发者往往要面对诸多繁杂的配置工作,这些配置工作不仅耗时费力,还容易出错,严重影响开发效率。
管理 jar 包依赖就是一项让人头疼的任务。在传统 Spring 项目中,我们需要手动引入项目所需的各种 jar 包,并且要确保各个 jar 包之间的版本兼容性 。一旦出现版本冲突,排查和解决问题就会变得异常困难。比如,在一个包含 Spring MVC、Spring Data JPA 和 Spring Security 等多个模块的项目中,需要引入大量相关的 jar 包,每个 jar 包可能又依赖其他的子 jar 包,稍有不慎就可能导致依赖冲突,像不同版本的 Spring 核心库之间的冲突,会引发各种难以调试的错误。
维护 web.xml 和 Dispatch - Servlet.xml 等配置文件也是一大痛点。在 web.xml 中,我们要配置 Servlet、Filter、Listener 等组件,定义它们的初始化参数、映射路径等 。例如,配置一个简单的 DispatcherServlet,就需要在 web.xml 中编写如下代码:
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc - servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
而在 Dispatch - Servlet.xml(通常是 springmvc - servlet.xml)中,又要配置视图解析器、处理器映射器、处理器适配器等 Spring MVC 的核心组件 。配置视图解析器时,可能需要类似这样的配置:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB - INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
随着项目规模的增大,这些配置文件会变得越来越臃肿,维护成本也越来越高。而且,不同的配置项之间可能存在复杂的依赖关系,修改一处配置可能会影响到其他部分的功能,这使得配置的管理变得非常困难。
(二)Spring Boot 如何简化配置
Spring Boot 通过引入自动配置和默认约定,彻底改变了这种繁琐的开发局面,让开发者能够从配置的泥潭中解脱出来,专注于业务逻辑的实现。
在依赖管理方面,Spring Boot 引入了 Starter 的概念 。Starter 是一组预定义的依赖集合,它将项目开发中常用的依赖进行了整合和封装。比如,当我们开发一个 Spring Boot Web 应用时,只需要在 pom.xml 文件中添加 spring-boot-starter-web 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 会自动管理该 Starter 所依赖的所有 jar 包版本,包括 Spring MVC、Tomcat(嵌入式 Servlet 容器)等相关依赖,无需我们手动去指定每个 jar 包的版本,大大减少了版本冲突的可能性 。
对于配置文件 ,Spring Boot 有一套默认的约定 。它默认加载 application.properties 或 application.yml 文件作为配置文件,并且对很多配置项都提供了默认值。例如,默认的 Web 服务器端口是 8080,如果我们没有在配置文件中指定端口号,应用就会使用这个默认端口启动 。在数据库连接配置方面,Spring Boot 也提供了默认的数据源配置,如果我们使用的是常见的数据库(如 MySQL、Oracle 等),只需要在配置文件中简单地配置数据库的连接信息,如用户名、密码、URL 等,Spring Boot 就会自动帮我们创建数据源和相关的数据库连接配置。例如,在 application.properties 中配置 MySQL 连接:
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Spring Boot 会根据这些配置自动创建数据源,并将其注入到 Spring 容器中,供其他组件使用 .Spring Boot 还通过自动配置机制,根据项目中引入的依赖和配置文件,自动创建和配置 Spring 容器中的各种 Bean 。
例如,当我们引入了 spring-boot-starter-web 依赖后,Spring Boot 会自动配置 DispatcherServlet、视图解析器、处理器映射器等 Spring MVC 的核心组件,无需我们再像传统 Spring 开发那样在 XML 文件中进行繁琐的配置 。这一切都是基于 Spring Boot 的自动配置类和条件注解(如 @ConditionalOnClass、@ConditionalOnProperty 等)实现的,这些条件注解会根据类路径下是否存在某个类、配置文件中是否存在某个属性等条件来决定是否创建和配置某个 Bean。
(三)Starter 依赖机制
Starter 是 Spring Boot 的一项重要特性,它极大地简化了项目的依赖管理 。Spring Boot 将各种常见的应用场景封装成一个个的 Starter,每个 Starter 都是一组相关的依赖集合 。例如,spring-boot-starter-web是用于开发 Web 应用的 Starter,它包含了 Spring MVC、Tomcat(嵌入式 Servlet 容器)等相关依赖;spring-boot-starter-data-jpa是用于开发 JPA(Java Persistence API)数据访问层的 Starter,它包含了 Spring Data JPA、Hibernate 等相关依赖 。
当我们在项目中添加某个 Starter 依赖时,只需要在pom.xml(Maven 项目)或build.gradle(Gradle 项目)文件中添加相应的依赖坐标,Spring Boot 会自动管理该 Starter 所依赖的所有 jar 包版本,避免了手动管理依赖版本带来的麻烦 。例如,在 Maven 项目中添加spring-boot-starter-web依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
添加这个依赖后,Spring Boot 会自动引入 Spring MVC、Tomcat 等相关的 jar 包,并且会根据项目的需要自动配置好这些组件 。我们无需关心这些组件之间的依赖关系和版本兼容性,只需要专注于业务逻辑的开发 。这种方式不仅减少了配置工作量,还降低了因依赖冲突导致的问题发生概率 。
(四)自动装配机制
Spring Boot 的自动装配机制是 “约定优于配置” 理念的核心体现之一 。它通过扫描类路径下的META-INF/spring.factories文件,自动配置 Spring 容器中的 Bean 。
在每个 Spring Boot Starter 的 META-INF/spring.factories 文件中,定义了一系列的自动配置类 。例如,spring-boot-starter-web的spring.factories文件中定义了WebMvcAutoConfiguration等自动配置类,这些配置类负责创建和配置 Spring MVC 相关的 Bean,如DispatcherServlet、HandlerMapping、ViewResolver等 。当我们在项目中引入spring-boot-starter-web依赖后,Spring Boot 在启动时会扫描META-INF/spring.factories文件,找到与WebMvcAutoConfiguration相关的配置,并根据项目的实际情况(如是否存在相关的类、配置文件中的属性等)决定是否创建和配置这些 Bean 。
自动装配的核心原理是基于 Spring 的条件注解@ConditionalOnClass@ConditionalOnProperty等 。@ConditionalOnClass表示只有当类路径下存在某个类时,才会创建和配置相关的 Bean;@ConditionalOnProperty表示只有当配置文件中存在某个属性时,才会创建和配置相关的 Bean 。例如,WebMvcAutoConfiguration类上使用了@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })注解,这意味着只有当类路径下存在Servlet、DispatcherServlet和WebMvcConfigurer这三个类时,才会创建和配置 Spring MVC 相关的 Bean 。通过这种方式,Spring Boot 实现了根据项目的依赖和配置自动创建和配置 Bean,大大减少了开发者手动配置的工作量 。
四、优势
(一)提高开发效率
在传统的 Spring 开发中,开发者花费大量时间在各种配置文件的编写和调试上,往往项目还未开始核心业务开发,就已经被配置工作搞得疲惫不堪。而 Spring Boot 的 “约定优于配置” 理念,就像是为开发者打造了一条高速公路,让开发过程变得顺畅无阻。
以搭建一个简单的 Web 应用为例,在传统 Spring 开发中,光是配置 DispatcherServlet、视图解析器、各种过滤器等,就需要编写大量的 XML 配置代码,并且要确保各个配置项之间的逻辑关系正确无误。而在 Spring Boot 中,我们只需要引入spring-boot-starter-web依赖,Spring Boot 就会自动帮我们完成这些配置工作,开发者可以在短短几分钟内就搭建好一个基本的 Web 应用框架,迅速投入到业务逻辑的开发中。这种方式大大减少了配置时间,让开发效率得到了质的飞跃。
(二)增强代码的可维护性
统一的约定使得 Spring Boot 项目的结构和配置更加清晰、规范 。在一个遵循 Spring Boot 约定的项目中,团队成员可以很容易地找到他们需要的代码和配置文件。例如,按照约定的项目结构,控制器层的代码位于controller包下,服务层的代码位于service包下,数据访问层的代码位于repository包下,配置文件位于resources目录下。这种清晰的结构使得代码的可读性大大提高,当需要修改或扩展某个功能时,开发者可以快速定位到相关的代码和配置。
而且,由于大部分配置都是由框架自动完成的,开发者只需要关注自己的业务逻辑和少量的特殊配置,减少了配置文件中冗余和复杂的配置项,降低了配置出错的概率 。当项目规模逐渐增大时,这种优势更加明显,能够有效降低维护成本,提高项目的可维护性。
(三)促进团队协作
在团队开发中,一致的约定就像是一套通用的语言,让团队成员之间的沟通和协作变得更加顺畅 。如果每个成员都按照自己的方式进行配置和开发,那么在代码合并和集成时,很容易出现各种问题,比如配置冲突、依赖版本不一致等。而 Spring Boot 的 “约定优于配置”,让团队成员遵循相同的规则进行开发,减少了因配置差异而产生的问题 。
例如,在一个多人开发的 Spring Boot 项目中,大家都使用默认的配置文件格式和位置,按照约定的项目结构进行代码组织。这样,新加入的成员可以快速熟悉项目的结构和配置方式,融入团队开发中 。在进行代码审查时,也更容易理解其他成员的代码逻辑,提高了团队协作的效率,有利于项目的顺利推进