【轻松入门SpringBoot】从0到1搭建web 工程(下)-在实践中对比SpringBoot和Spring框架

系列文章:

【轻松入门SpringBoot】从0到1搭建web 工程(上)-使用SpringBoot框架

【轻松入门SpringBoot】从0到1搭建web 工程(中) -使用Spring框架

一、前言

前面用了两篇文章讲述了如何使用 Spring 框架、SpringBoot 框架分别搭建 web 工程,我们发现在业务实现、工能测试中,两种框架的实现方案大致相同,但配置方面却大相径庭。这篇文章,我将基于实践对比这两种框架的核心差异、设计理念,说明 SpringBoot 为何成为主流。

二、核心共识

SpringBoot 框架是在Spring 框架的基础上封装和配置的框架,是Spring 框架的加强版,而非替代版。SpringBoot 框架 = Spring 框架+ 自动配置 + 起步依赖 + 嵌入式服务器,SpringBoot 框架也是遵循 Spring 的 AOP、IOC 的核心思想的。

三、对比

一张图胜过千言万语

|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 对比维度 | Spring | SpringBoot | 核心差别 |
| 业务层代码 | 使用@Controller、@Service 标识 bean;使用@RequestMapping 标识映射;使用@Autowired引入依赖等 | 完全一样 | 无差别 |
| 测试-非web | @RunWith(SpringRunner.class) @ContextConfiguration(locations = {"classpath:spring-context.xml"}) | @RunWith(SpringRunner.class) @SpringBootTest | 1、都需要@RunWith注解表示加载 Spring 容器,扫描 bean; 2、@ContextConfiguration(locations = {"classpath:spring-context.xml"})手动指定需扫描的配置文件地址; 3、@SpringBootTest自动扫描主启动类所在包,加载所有配置,无需手动指定。 |
| 测试-web | @RunWith(SpringRunner.class) @ContextConfiguration(locations = {"classpath:spring-context.xml", "classpath:spring-mvc.xml"}) @WebAppConfiguration - 额外操作:手动构建 MockMvc(MockMvcBuilders.webAppContextSetup(webApplicationContext).build()) | @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc | 核心逻辑一致。 1、Spring需要显式添加扫描 SpringMVC 相关配置路径,手动构建 mockMVC; 2、SpringBoot不用手动指定多个 XML 配置,不用手动构建 MockMvc; |
| 核心配置 | spring-context.xml, spring-mvc.xml, web.xml(web 工程需要) | application.yml | Spring框架需要手动配置配置文件 Spring-context,Spring-mvc,web.xml来管理数据源配置、组件扫描、事务管理、JPA 管理、DispatcherServlet。并且需要手动配置每一个maven 依赖以及maven 版本,需要开发自己解决依赖冲突问题。 SpringBoot 框架,"约定大于配置",无 xml 配置,使用 application.yml 或 application.properties。 自动扫描根目录路径下的Spring 资源,自动配置 dispatcherServlet等,只需要配置数据库的 URL,密码等需要自定义的信息。 且有起步依赖,如 spring-boot-starter-web、spring-boot-starter-data-jpa,一站式配齐,减少"缺这少那"的可能,并且自动管理依赖版本,无需手动匹配等。 |
| 服务启动 | -web 工程 1、打成 war 包 2、安装并配置 Tomcat,将 war 部署到tomcat | -web 工程 1、打成 jar 包(可执行 jar) 2、内置Tomcat+自动配置。 本地执行SpringBootUniqApplication的 main 方法,直接启动 | SpringBoot 内置 web 服务器+自动配置,无需手动安装和配置 web 服务器。 |

四、本质

经过前面的对比,我们可以体会到SpringBoot 框架的三大特点:

1、起步依赖-Starter Dependencies

Spring需要挨个引入每个依赖,上篇文章一个简单的 web 服务搭建完,手动引入了 13 个核心依赖,同时还需要找合适的版本,手动解决版本冲突。使用 SpringBoot 一站式配套依赖spring-boot-starter-XX,可以一键引入搭配好的依赖包,既解决了丢三落四的问题,又调试了好了版本依赖。

2、自动检测+默认配置

Spring 核心配置文件 spring-context.xml,spring-mvc.xml,web.xml

2.1 src/main/resources/spring-context.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--Spring容器配置文件 配置 JPA、数据源、事务管理、Bean 扫描-->

    <!-- 1.扫描 service、Repository 等 bean(排除 Controller,由 Spring mvc 扫描) -->
    <context:component-scan base-package="com.example.uniq">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 2.配置数据源(Spring Boot 用 application.yml配置,自动创建 DataSource) -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!--使用 MySQL 驱动-->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3307/boot_demo?useSSL=false&amp;serverTimezone=Asia/Shanghai&amp;createDatabaseIfNotExist=true&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- 3.配置 JPA 实体管理器工厂  -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.example.uniq.entity"/> <!-- 扫描JPA实体类 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true"/> <!-- 日志中打印 SQL -->
                <property name="generateDdl" value="true"/> <!-- 自动创建表结构 (类似 JPA ddl-auto: update)-->
<!--                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> &lt;!&ndash; 数据库方言 &ndash;&gt;-->
            </bean>
        </property>
        <!-- 新增:Hibernate 额外配置(解决 MySQL 8.0 建表语法错误) -->
        <property name="jpaProperties">
            <props>
                <!-- 强制使用 InnoDB 引擎(MySQL 8.0 默认引擎),避免生成 type=MyISAM -->
                <prop key="hibernate.dialect.storage_engine">innodb</prop>
                <!-- 可选:明确指定建表引擎为 InnoDB,进一步兼容 -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!-- 4.配置JPA事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!-- 5.开启事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 6.配置 Spring Data JPA (扫描 Repository 接口) -->
    <jpa:repositories base-package="com.example.uniq.repository"
    entity-manager-factory-ref="entityManagerFactory"
    transaction-manager-ref="transactionManager"/>
</beans>
2.2 src/main/resources/spring-mvc.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--   1.SpringMVC 配置文件 配置 controller 扫描、 视图解析、json 消息转换器-->
    <context:component-scan base-package="com.example.uniq.controller" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 2.启动 Springmvc 注解驱动(支持@RequestMapping @ResponseBody) -->
    <mvc:annotation-driven>
        <!-- 配置 json 消息转换器 controller返回 json 必须 -->
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 3.静态资源处理 -->
    <mvc:default-servlet-handler/>
</beans>
2.3 src/main/webapp/WEB-INF/web.xml
XML 复制代码
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!--1.加载 Spring 容器配置文件(spring-context.xml) -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--2.Spring MVC 配置 前端控制器 (DispatcherServlet)-->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value> <!-- Spring MVC 容器配置文件 -->
        </init-param>
        <load-on-startup>1</load-on-startup> <!-- 启动时加载 -->
    </servlet>
    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <url-pattern>/</url-pattern> <!-- 拦截所有请求 -->
    </servlet-mapping>

    <!--3.配置字符串编码过滤器 (避免中文乱码) -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

SpringBoot 核心配置 application.yml

java 复制代码
#服务器配置
server:
  port: 8080 #端口号 项目启动端口

# Spring 核心配置
spring:
  # 数据库连接配置
  datasource:
    url: jdbc:mysql://localhost:3307/boot_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver #  对应的 Mysql 驱动
  # JPA配置(操作数据库)
  jpa:
    hibernate:
      ddl-auto: update # 自动创建/更新表结构 (开发环境用,生产环境改 none)
    show-sql: true # 控制台打印 sql 语句
    properties:
      hibernate:
        format_sql: true # 格式化 sql

Spring 核心配置文件 3个,SpringBoot 核心配置文件 1 个,是SpringBoot框架不需要管理这些关联吗?当然不是。有句话说"你觉得轻松,因为有人替你负重前行",在这里就是"SpringBoot框架做负重了"。

SpringBoot基于"约定大于配置"的思想配置了默认值、做了套装。自动检测机制使引入Spring-boot-starter-XX后,jar包内有核心配置类,会自动解析配置,注册bean到Spring容器等。

举个栗子:在Spring框架中,需要使用Spring-context.xml配置扫描Bean的范围:

XML 复制代码
<!-- 1.扫描 service、Repository 等 bean(排除 Controller,由 Spring mvc 扫描) -->
    <context:component-scan base-package="com.example.uniq">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

SpringBoot 没有了Spring-context.xml,是如何扫描 Bean 的呢?Bean扫描自动化:默认扫描主启动类所在包(@SpringBootApplication 包含 @ComponentScan),无需手动指定扫描路径;

对于需要自定义的部分,比如配置数据源,SpringBoot 又是怎样封装的呢?

引入spring-boot-starter-data-jpa/jdbc后,jar 中的DataSourceAutoConfiguration自动解析application.xml中的spring.datasource配置,创建数据源Bean。

所有自动配置的触发,都源于@SpringBootApplication中的@EnableAutoConfiguration注解,每个自动配置类通过条件注解实现按需配置等。但这部分不仅仅是工程层面,还涉及到原理,这篇文章我们先了解到是 SpringBoot 框架做了自动检测、默认配置等,原理可能需要结合源码,为了文章结构清晰,易读性,原理我们放到后面的章节。

3、内置 web 服务器

我们发现 SpringBoot框架的 web 工程中,跟 web相关的好像只添加了spring-boot-starter-web 依赖,在 application.yml 配置了server.port:8080。既没手动引入javax.servlet-api,也没配置 Tomcat,直接在 SpringBootUniqApplication 的main 方法中就能启动服务。这样如此简单,一方面得益于前面说的SpringBoot 框架的自动配置,另一方面是 SpringBoot 内置 web 服务器。引入spring-boot-starter-web 后,TomcatAutoConfiguration 自动启动嵌入式 Tomcat(默认 8080 端口),无需部署 War 包到外部 Tomcat。

五、总结

在本章中,我们从工程的角度,横向对比了Spring 框架和 SpringBoot 框架搭建 web 工程需要引入的依赖和配置,直观感受到了使用 SpringBoot 框架上手的高效和轻松。同时也知道,有些配置虽然我们没有手动添加,但不是没有用了,而是由SpringBoot 框架做了自动检测和默认配置。

到这里,读者可能有疑问:spring-boot-starter-XX 高度封装,一键打包,是否会把我不需要的类添加我的工程?自己做饭不吃香菜可以不加,预制菜料包一大包混在一起,我还能不要香菜吗?或者加点别的吗?内置 web 服务器默认端口是 8080,但我有服务已经占用 8080 端口了,可以不用换成别的吗?挠头。。

后面我们一起学习来揭晓答案吧,bye~~

相关推荐
短剑重铸之日16 小时前
7天读懂MySQL|Day 5:执行引擎与SQL优化
java·数据库·sql·mysql·架构
酒九鸠玖16 小时前
Java--多线程
java
Dreamboat-L16 小时前
云服务器上部署nginx
java·服务器·nginx
长安er16 小时前
LeetCode215/347/295 堆相关理论与题目
java·数据结构·算法·leetcode·
cici1587417 小时前
C#实现三菱PLC通信
java·网络·c#
k***921618 小时前
【C++】继承和多态扩展学习
java·c++·学习
weixin_4407305018 小时前
java结构语句学习
java·开发语言·学习
JIngJaneIL18 小时前
基于java+ vue医院管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
Coder_Boy_18 小时前
Spring AI 源码大白话解析
java·人工智能·spring
仙俊红18 小时前
在 Java 中,`==` 和 `equals()` 的区别
java·开发语言·jvm